aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.hgignore3
-rw-r--r--buildconfigs/linux-defconfig_xen0_ia642
-rw-r--r--buildconfigs/linux-defconfig_xen_ia642
-rw-r--r--config/SunOS.mk2
-rw-r--r--docs/src/user.tex26
-rw-r--r--docs/xen-api/Makefile23
-rw-r--r--docs/xen-api/coversheet.tex50
-rw-r--r--docs/xen-api/fdl.tex488
-rw-r--r--docs/xen-api/presentation.tex149
-rw-r--r--docs/xen-api/todo.tex140
-rw-r--r--docs/xen-api/vm-lifecycle.tex24
-rw-r--r--docs/xen-api/vm_lifecycle.dot15
-rw-r--r--docs/xen-api/wire-protocol.tex287
-rw-r--r--docs/xen-api/xen.eps44
-rw-r--r--docs/xen-api/xenapi-coversheet.tex40
-rw-r--r--docs/xen-api/xenapi-datamodel-graph.dot17
-rw-r--r--docs/xen-api/xenapi-datamodel.tex9648
-rw-r--r--docs/xen-api/xenapi.tex56
-rw-r--r--linux-2.6-xen-sparse/arch/i386/kernel/sysenter.c2
-rw-r--r--linux-2.6-xen-sparse/arch/i386/mm/hypervisor.c4
-rw-r--r--linux-2.6-xen-sparse/arch/i386/mm/init-xen.c4
-rw-r--r--linux-2.6-xen-sparse/arch/ia64/xen/xencomm.c5
-rw-r--r--linux-2.6-xen-sparse/arch/x86_64/mm/init-xen.c4
-rw-r--r--linux-2.6-xen-sparse/drivers/char/tpm/tpm_xen.c1
-rw-r--r--linux-2.6-xen-sparse/drivers/xen/blkback/blkback.c42
-rw-r--r--linux-2.6-xen-sparse/drivers/xen/blkback/common.h5
-rw-r--r--linux-2.6-xen-sparse/drivers/xen/blkback/vbd.c3
-rw-r--r--linux-2.6-xen-sparse/drivers/xen/blkback/xenbus.c21
-rw-r--r--linux-2.6-xen-sparse/drivers/xen/blkfront/blkfront.c30
-rw-r--r--linux-2.6-xen-sparse/drivers/xen/blkfront/block.h2
-rw-r--r--linux-2.6-xen-sparse/drivers/xen/blkfront/vbd.c75
-rw-r--r--linux-2.6-xen-sparse/drivers/xen/blktap/blktap.c195
-rw-r--r--linux-2.6-xen-sparse/drivers/xen/blktap/xenbus.c6
-rw-r--r--linux-2.6-xen-sparse/drivers/xen/core/Makefile2
-rw-r--r--linux-2.6-xen-sparse/drivers/xen/core/machine_reboot.c185
-rw-r--r--linux-2.6-xen-sparse/drivers/xen/core/reboot.c210
-rw-r--r--linux-2.6-xen-sparse/drivers/xen/netback/interface.c3
-rw-r--r--linux-2.6-xen-sparse/drivers/xen/netback/netback.c9
-rw-r--r--linux-2.6-xen-sparse/drivers/xen/netback/xenbus.c14
-rw-r--r--linux-2.6-xen-sparse/drivers/xen/netfront/netfront.c48
-rw-r--r--linux-2.6-xen-sparse/drivers/xen/privcmd/privcmd.c18
-rw-r--r--linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_comms.c7
-rw-r--r--linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/fixmap.h1
-rw-r--r--linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/hypercall.h10
-rw-r--r--linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/hypervisor.h8
-rw-r--r--linux-2.6-xen-sparse/include/asm-i386/mach-xen/setup_arch_post.h15
-rw-r--r--linux-2.6-xen-sparse/include/asm-ia64/hypercall.h3
-rw-r--r--linux-2.6-xen-sparse/include/asm-ia64/hypervisor.h16
-rw-r--r--linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/fixmap.h1
-rw-r--r--linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/hypercall.h10
-rw-r--r--linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/setup_arch_post.h17
-rw-r--r--linux-2.6-xen-sparse/include/xen/gnttab.h5
-rw-r--r--patches/linux-2.6.16.29/series1
-rw-r--r--patches/linux-2.6.16.29/xenoprof-generic.patch91
-rw-r--r--tools/Makefile1
-rw-r--r--tools/blktap/drivers/blktapctrl.c4
-rw-r--r--tools/blktap/drivers/tapdisk.c5
-rw-r--r--tools/blktap/drivers/tapdisk.h1
-rw-r--r--tools/blktap/lib/blktaplib.h7
-rw-r--r--tools/console/Makefile2
-rw-r--r--tools/examples/blktap18
-rw-r--r--tools/examples/block30
-rw-r--r--tools/examples/external-device-migrate56
-rwxr-xr-xtools/examples/vif-bridge2
-rw-r--r--tools/examples/vif-nat4
-rwxr-xr-xtools/examples/vif-route2
-rw-r--r--tools/examples/xend-config.sxp2
-rw-r--r--tools/examples/xmexample.hvm3
-rw-r--r--tools/firmware/Makefile1
-rw-r--r--tools/firmware/acpi/Makefile68
-rw-r--r--tools/firmware/acpi/acpi2_0.h331
-rw-r--r--tools/firmware/acpi/acpi_build.c232
-rw-r--r--tools/firmware/acpi/acpi_facs.c72
-rw-r--r--tools/firmware/acpi/acpi_facs.h32
-rw-r--r--tools/firmware/acpi/acpi_fadt.c193
-rw-r--r--tools/firmware/acpi/acpi_fadt.h166
-rw-r--r--tools/firmware/acpi/acpi_madt.c68
-rw-r--r--tools/firmware/acpi/acpi_rsdt.c68
-rw-r--r--tools/firmware/hvmloader/Makefile11
-rw-r--r--tools/firmware/hvmloader/acpi/Makefile63
-rw-r--r--tools/firmware/hvmloader/acpi/README (renamed from tools/firmware/acpi/README)0
-rw-r--r--tools/firmware/hvmloader/acpi/acpi2_0.h324
-rw-r--r--tools/firmware/hvmloader/acpi/build.c241
-rw-r--r--tools/firmware/hvmloader/acpi/dsdt.asl (renamed from tools/firmware/acpi/acpi_dsdt.asl)0
-rw-r--r--tools/firmware/hvmloader/acpi/dsdt.c (renamed from tools/firmware/acpi/acpi_dsdt.c)0
-rw-r--r--tools/firmware/hvmloader/acpi/gen.c (renamed from tools/firmware/acpi/acpi_gen.c)0
-rw-r--r--tools/firmware/hvmloader/acpi/static_tables.c184
-rw-r--r--tools/firmware/hvmloader/acpi_madt.c182
-rw-r--r--tools/firmware/hvmloader/acpi_ssdt_tpm.asl29
-rw-r--r--tools/firmware/hvmloader/acpi_ssdt_tpm.h25
-rw-r--r--tools/firmware/hvmloader/acpi_utils.c207
-rw-r--r--tools/firmware/hvmloader/acpi_utils.h (renamed from tools/firmware/acpi/acpi_madt.h)38
-rw-r--r--tools/firmware/hvmloader/hvmloader.c16
-rw-r--r--tools/firmware/hvmloader/util.c3
-rw-r--r--tools/firmware/vmxassist/setup.c3
-rw-r--r--tools/firmware/vmxassist/vm86.c87
-rw-r--r--tools/ioemu/Makefile.target1
-rw-r--r--tools/ioemu/hw/ne2000.c35
-rw-r--r--tools/ioemu/hw/pc.c3
-rw-r--r--tools/ioemu/hw/serial.c44
-rw-r--r--tools/ioemu/hw/tpm_tis.c1114
-rw-r--r--tools/ioemu/keymaps/ja3
-rw-r--r--tools/ioemu/target-i386-dm/cpu.h2
-rw-r--r--tools/ioemu/target-i386-dm/exec-dm.c50
-rw-r--r--tools/ioemu/target-i386-dm/helper2.c113
-rw-r--r--tools/ioemu/target-i386-dm/i8259-dm.c42
-rw-r--r--tools/ioemu/target-i386-dm/qemu-dm.debug7
-rw-r--r--tools/ioemu/vl.c21
-rw-r--r--tools/ioemu/vl.h23
-rw-r--r--tools/ioemu/vnc_keysym.h10
-rw-r--r--tools/ioemu/xenstore.c137
-rw-r--r--tools/libfsimage/Makefile13
-rw-r--r--tools/libfsimage/Rules.mk32
-rwxr-xr-xtools/libfsimage/check-libext2fs21
-rw-r--r--tools/libfsimage/common/Makefile46
-rw-r--r--tools/libfsimage/common/fsimage.c142
-rw-r--r--tools/libfsimage/common/fsimage.h52
-rw-r--r--tools/libfsimage/common/fsimage_grub.c276
-rw-r--r--tools/libfsimage/common/fsimage_grub.h92
-rw-r--r--tools/libfsimage/common/fsimage_plugin.c214
-rw-r--r--tools/libfsimage/common/fsimage_plugin.h65
-rw-r--r--tools/libfsimage/common/fsimage_priv.h62
-rw-r--r--tools/libfsimage/common/mapfile-GNU37
-rw-r--r--tools/libfsimage/common/mapfile-SunOS35
-rw-r--r--tools/libfsimage/ext2fs-lib/Makefile15
-rw-r--r--tools/libfsimage/ext2fs-lib/ext2fs-lib.c172
-rw-r--r--tools/libfsimage/ext2fs/Makefile13
-rw-r--r--tools/libfsimage/ext2fs/fsys_ext2fs.c804
-rw-r--r--tools/libfsimage/reiserfs/Makefile13
-rw-r--r--tools/libfsimage/reiserfs/fsys_reiserfs.c1254
-rw-r--r--tools/libfsimage/ufs/Makefile13
-rw-r--r--tools/libfsimage/ufs/fsys_ufs.c276
-rw-r--r--tools/libfsimage/ufs/ufs.h228
-rw-r--r--tools/libxc/ia64/xc_ia64_hvm_build.c2
-rw-r--r--tools/libxc/xc_core.c2
-rw-r--r--tools/libxc/xc_domain.c28
-rw-r--r--tools/libxc/xc_hvm_build.c285
-rw-r--r--tools/libxc/xc_linux_build.c183
-rw-r--r--tools/libxc/xc_linux_save.c24
-rw-r--r--tools/libxc/xc_misc.c28
-rw-r--r--tools/libxc/xc_private.c22
-rw-r--r--tools/libxc/xc_private.h5
-rw-r--r--tools/libxc/xc_ptrace.c29
-rw-r--r--tools/libxc/xc_ptrace_core.c24
-rw-r--r--tools/libxc/xenctrl.h31
-rw-r--r--tools/libxc/xenguest.h34
-rw-r--r--tools/libxc/xg_private.c58
-rw-r--r--tools/libxc/xg_private.h2
-rw-r--r--tools/libxen/COPYING510
-rw-r--r--tools/libxen/Makefile37
-rw-r--r--tools/libxen/README54
-rw-r--r--tools/libxen/include/xen_boot_type.h87
-rw-r--r--tools/libxen/include/xen_boot_type_internal.h37
-rw-r--r--tools/libxen/include/xen_common.h145
-rw-r--r--tools/libxen/include/xen_cpu_feature.h387
-rw-r--r--tools/libxen/include/xen_cpu_feature_internal.h37
-rw-r--r--tools/libxen/include/xen_driver_type.h77
-rw-r--r--tools/libxen/include/xen_driver_type_internal.h37
-rw-r--r--tools/libxen/include/xen_host.h292
-rw-r--r--tools/libxen/include/xen_host_cpu.h239
-rw-r--r--tools/libxen/include/xen_host_cpu_decl.h30
-rw-r--r--tools/libxen/include/xen_host_decl.h30
-rw-r--r--tools/libxen/include/xen_int_float_map.h53
-rw-r--r--tools/libxen/include/xen_internal.h193
-rw-r--r--tools/libxen/include/xen_network.h273
-rw-r--r--tools/libxen/include/xen_network_decl.h30
-rw-r--r--tools/libxen/include/xen_on_crash_behaviour.h97
-rw-r--r--tools/libxen/include/xen_on_crash_behaviour_internal.h38
-rw-r--r--tools/libxen/include/xen_on_normal_exit.h77
-rw-r--r--tools/libxen/include/xen_on_normal_exit_internal.h37
-rw-r--r--tools/libxen/include/xen_pif.h290
-rw-r--r--tools/libxen/include/xen_pif_decl.h30
-rw-r--r--tools/libxen/include/xen_sr.h282
-rw-r--r--tools/libxen/include/xen_sr_decl.h30
-rw-r--r--tools/libxen/include/xen_string_string_map.h53
-rw-r--r--tools/libxen/include/xen_user.h204
-rw-r--r--tools/libxen/include/xen_user_decl.h30
-rw-r--r--tools/libxen/include/xen_vbd.h285
-rw-r--r--tools/libxen/include/xen_vbd_decl.h30
-rw-r--r--tools/libxen/include/xen_vbd_mode.h77
-rw-r--r--tools/libxen/include/xen_vbd_mode_internal.h37
-rw-r--r--tools/libxen/include/xen_vdi.h344
-rw-r--r--tools/libxen/include/xen_vdi_decl.h30
-rw-r--r--tools/libxen/include/xen_vdi_type.h82
-rw-r--r--tools/libxen/include/xen_vdi_type_internal.h37
-rw-r--r--tools/libxen/include/xen_vif.h305
-rw-r--r--tools/libxen/include/xen_vif_decl.h30
-rw-r--r--tools/libxen/include/xen_vm.h819
-rw-r--r--tools/libxen/include/xen_vm_decl.h30
-rw-r--r--tools/libxen/include/xen_vm_power_state.h97
-rw-r--r--tools/libxen/include/xen_vm_power_state_internal.h37
-rw-r--r--tools/libxen/include/xen_vtpm.h216
-rw-r--r--tools/libxen/include/xen_vtpm_decl.h31
-rw-r--r--tools/libxen/src/xen_boot_type.c83
-rw-r--r--tools/libxen/src/xen_common.c1363
-rw-r--r--tools/libxen/src/xen_cpu_feature.c143
-rw-r--r--tools/libxen/src/xen_driver_type.c81
-rw-r--r--tools/libxen/src/xen_host.c390
-rw-r--r--tools/libxen/src/xen_host_cpu.c287
-rw-r--r--tools/libxen/src/xen_int_float_map.c37
-rw-r--r--tools/libxen/src/xen_network.c364
-rw-r--r--tools/libxen/src/xen_on_crash_behaviour.c85
-rw-r--r--tools/libxen/src/xen_on_normal_exit.c81
-rw-r--r--tools/libxen/src/xen_pif.c403
-rw-r--r--tools/libxen/src/xen_sr.c388
-rw-r--r--tools/libxen/src/xen_string_string_map.c49
-rw-r--r--tools/libxen/src/xen_user.c201
-rw-r--r--tools/libxen/src/xen_vbd.c387
-rw-r--r--tools/libxen/src/xen_vbd_mode.c81
-rw-r--r--tools/libxen/src/xen_vdi.c533
-rw-r--r--tools/libxen/src/xen_vdi_type.c82
-rw-r--r--tools/libxen/src/xen_vif.c440
-rw-r--r--tools/libxen/src/xen_vm.c1596
-rw-r--r--tools/libxen/src/xen_vm_power_state.c85
-rw-r--r--tools/libxen/src/xen_vtpm.c227
-rw-r--r--tools/libxen/test/test_bindings.c424
-rw-r--r--tools/pygrub/setup.py43
-rw-r--r--tools/pygrub/src/fsimage/fsimage.c299
-rw-r--r--tools/pygrub/src/fsys/__init__.py64
-rw-r--r--tools/pygrub/src/fsys/ext2/__init__.py38
-rw-r--r--tools/pygrub/src/fsys/ext2/ext2module.c387
-rw-r--r--tools/pygrub/src/fsys/ext2/test.py15
-rw-r--r--tools/pygrub/src/fsys/reiser/__init__.py40
-rw-r--r--tools/pygrub/src/fsys/reiser/reisermodule.c345
-rw-r--r--tools/pygrub/src/pygrub35
-rw-r--r--tools/python/README.XendConfig159
-rw-r--r--tools/python/README.sxpcfg116
-rw-r--r--tools/python/scripts/README49
-rw-r--r--tools/python/scripts/README.lifecycle136
-rw-r--r--tools/python/scripts/xapi.domcfg.py37
-rw-r--r--tools/python/scripts/xapi.py537
-rw-r--r--tools/python/scripts/xapi.vbdcfg.py12
-rw-r--r--tools/python/scripts/xapi.vdicfg.py7
-rw-r--r--tools/python/scripts/xapi.vifcfg.py10
-rw-r--r--tools/python/scripts/xapi.vtpmcfg.py3
-rw-r--r--tools/python/setup.py3
-rw-r--r--tools/python/xen/lowlevel/xc/xc.c88
-rw-r--r--tools/python/xen/util/blkif.py14
-rw-r--r--tools/python/xen/util/xmlrpclib2.py36
-rw-r--r--tools/python/xen/xend/Args.py2
-rw-r--r--tools/python/xen/xend/PrettyPrint.py2
-rw-r--r--tools/python/xen/xend/XendAPI.py1531
-rw-r--r--tools/python/xen/xend/XendAPIConstants.py75
-rw-r--r--tools/python/xen/xend/XendAuthSessions.py137
-rw-r--r--tools/python/xen/xend/XendBootloader.py6
-rw-r--r--tools/python/xen/xend/XendCheckpoint.py29
-rw-r--r--tools/python/xen/xend/XendConfig.py872
-rw-r--r--tools/python/xen/xend/XendConstants.py96
-rw-r--r--tools/python/xen/xend/XendDevices.py68
-rw-r--r--tools/python/xen/xend/XendDomain.py1157
-rw-r--r--tools/python/xen/xend/XendDomainInfo.py2238
-rw-r--r--tools/python/xen/xend/XendError.py16
-rw-r--r--tools/python/xen/xend/XendNode.py129
-rw-r--r--tools/python/xen/xend/XendProtocol.py2
-rw-r--r--tools/python/xen/xend/XendRoot.py28
-rw-r--r--tools/python/xen/xend/XendStorageRepository.py381
-rw-r--r--tools/python/xen/xend/XendVDI.py155
-rw-r--r--tools/python/xen/xend/image.py82
-rw-r--r--tools/python/xen/xend/server/DevController.py55
-rw-r--r--tools/python/xen/xend/server/SrvDaemon.py10
-rw-r--r--tools/python/xen/xend/server/SrvDomainDir.py2
-rw-r--r--tools/python/xen/xend/server/SrvServer.py30
-rw-r--r--tools/python/xen/xend/server/XMLRPCServer.py97
-rw-r--r--tools/python/xen/xend/server/blkif.py48
-rw-r--r--tools/python/xen/xend/server/iopif.py4
-rw-r--r--tools/python/xen/xend/server/irqif.py2
-rw-r--r--tools/python/xen/xend/server/netif.py38
-rw-r--r--tools/python/xen/xend/server/pciif.py59
-rw-r--r--tools/python/xen/xend/server/tpmif.py32
-rw-r--r--tools/python/xen/xend/sxp.py24
-rw-r--r--tools/python/xen/xend/uuid.py10
-rw-r--r--tools/python/xen/xm/create.py84
-rw-r--r--tools/python/xen/xm/main.py102
-rw-r--r--tools/python/xen/xm/new.py68
-rw-r--r--tools/xenmon/Makefile2
-rw-r--r--tools/xenmon/xenmon.py4
-rw-r--r--tools/xenstat/libxenstat/src/xenstat.c20
-rw-r--r--tools/xenstore/Makefile2
-rw-r--r--tools/xenstore/xsls.c19
-rw-r--r--tools/xentrace/formats2
-rw-r--r--tools/xm-test/configure.ac9
-rw-r--r--tools/xm-test/lib/XmTestLib/arch.py1
-rw-r--r--tools/xm-test/lib/XmTestReport/arch.py4
-rw-r--r--tools/xm-test/ramdisk/Makefile.am6
-rw-r--r--tools/xm-test/ramdisk/README-XenSource-initrd-1.0-img3
-rw-r--r--tools/xm-test/ramdisk/README-XenSource-initrd-1.1-img45
-rw-r--r--tools/xm-test/ramdisk/make-release.sh47
-rw-r--r--tools/xm-test/ramdisk/patches/buildroot/add_xvd_devices.patch13
-rwxr-xr-xtools/xm-test/runtest.sh25
-rw-r--r--tools/xm-test/tests/block-create/01_block_attach_device_pos.py10
-rw-r--r--tools/xm-test/tests/block-create/02_block_attach_file_device_pos.py8
-rw-r--r--tools/xm-test/tests/block-create/04_block_attach_device_repeatedly_pos.py16
-rw-r--r--tools/xm-test/tests/block-create/05_block_attach_and_dettach_device_repeatedly_pos.py18
-rw-r--r--tools/xm-test/tests/block-create/06_block_attach_baddomain_neg.py8
-rw-r--r--tools/xm-test/tests/block-create/07_block_attach_baddevice_neg.py14
-rw-r--r--tools/xm-test/tests/block-create/08_block_attach_bad_filedevice_neg.py16
-rw-r--r--tools/xm-test/tests/block-create/09_block_attach_and_dettach_device_check_data_pos.py44
-rw-r--r--tools/xm-test/tests/block-create/10_block_attach_dettach_multiple_devices.py30
-rw-r--r--tools/xm-test/tests/block-create/11_block_attach_shared_dom0.py2
-rw-r--r--tools/xm-test/tests/block-create/12_block_attach_shared_domU.py2
-rw-r--r--tools/xm-test/tests/block-destroy/01_block-destroy_btblock_pos.py8
-rw-r--r--tools/xm-test/tests/block-destroy/02_block-destroy_rtblock_pos.py8
-rw-r--r--tools/xm-test/tests/block-destroy/04_block-destroy_nonattached_neg.py2
-rw-r--r--tools/xm-test/tests/block-destroy/05_block-destroy_byname_pos.py8
-rw-r--r--tools/xm-test/tests/block-destroy/06_block-destroy_check_list_pos.py10
-rw-r--r--tools/xm-test/tests/block-integrity/01_block_device_read_verify.py4
-rw-r--r--tools/xm-test/tests/block-integrity/02_block_device_write_verify.py4
-rw-r--r--tools/xm-test/tests/block-list/01_block-list_pos.py6
-rw-r--r--tools/xm-test/tests/block-list/02_block-list_attachbd_pos.py6
-rw-r--r--tools/xm-test/tests/block-list/03_block-list_anotherbd_pos.py10
-rw-r--r--tools/xm-test/tests/block-list/06_block-list_checkremove_pos.py24
-rw-r--r--tools/xm-test/tests/create/07_create_mem64_pos.py2
-rw-r--r--tools/xm-test/tests/create/08_create_mem128_pos.py2
-rw-r--r--tools/xm-test/tests/create/09_create_mem256_pos.py2
-rw-r--r--tools/xm-test/tests/create/14_create_blockroot_pos.py11
-rw-r--r--tools/xm-test/tests/network-attach/04_network_attach_baddomain_neg.py6
-rw-r--r--unmodified_drivers/linux-2.6/Makefile1
-rw-r--r--unmodified_drivers/linux-2.6/compat-include/asm-generic/pgtable-nopud.h1
-rw-r--r--unmodified_drivers/linux-2.6/compat-include/xen/platform-compat.h15
-rwxr-xr-x[-rw-r--r--]unmodified_drivers/linux-2.6/mkbuildtree3
-rw-r--r--unmodified_drivers/linux-2.6/platform-pci/evtchn.c8
-rw-r--r--unmodified_drivers/linux-2.6/util/Kbuild3
-rw-r--r--unmodified_drivers/linux-2.6/util/Makefile3
-rw-r--r--xen/COPYING20
-rw-r--r--xen/arch/ia64/vmx/mmio.c16
-rw-r--r--xen/arch/ia64/vmx/vlsapic.c57
-rw-r--r--xen/arch/ia64/vmx/vmx_hypercall.c2
-rw-r--r--xen/arch/ia64/vmx/vmx_init.c11
-rw-r--r--xen/arch/ia64/vmx/vmx_process.c4
-rw-r--r--xen/arch/ia64/vmx/vmx_support.c25
-rw-r--r--xen/arch/ia64/xen/dom0_ops.c3
-rw-r--r--xen/arch/ia64/xen/domain.c102
-rw-r--r--xen/arch/ia64/xen/irq.c14
-rw-r--r--xen/arch/ia64/xen/mm.c77
-rw-r--r--xen/arch/ia64/xen/tlb_track.c5
-rw-r--r--xen/arch/ia64/xen/vcpu.c7
-rw-r--r--xen/arch/ia64/xen/xencomm.c7
-rw-r--r--xen/arch/ia64/xen/xensetup.c6
-rw-r--r--xen/arch/powerpc/domain.c23
-rw-r--r--xen/arch/powerpc/domain_build.c2
-rw-r--r--xen/arch/powerpc/mm.c8
-rw-r--r--xen/arch/powerpc/papr/xlate.c2
-rw-r--r--xen/arch/powerpc/setup.c9
-rw-r--r--xen/arch/powerpc/shadow.c4
-rw-r--r--xen/arch/powerpc/usercopy.c7
-rw-r--r--xen/arch/x86/domain.c109
-rw-r--r--xen/arch/x86/domain_build.c100
-rw-r--r--xen/arch/x86/domctl.c55
-rw-r--r--xen/arch/x86/e820.c2
-rw-r--r--xen/arch/x86/extable.c2
-rw-r--r--xen/arch/x86/hvm/hvm.c468
-rw-r--r--xen/arch/x86/hvm/i8254.c16
-rw-r--r--xen/arch/x86/hvm/i8259.c263
-rw-r--r--xen/arch/x86/hvm/instrlen.c5
-rw-r--r--xen/arch/x86/hvm/intercept.c79
-rw-r--r--xen/arch/x86/hvm/io.c133
-rw-r--r--xen/arch/x86/hvm/platform.c81
-rw-r--r--xen/arch/x86/hvm/pmtimer.c6
-rw-r--r--xen/arch/x86/hvm/rtc.c30
-rw-r--r--xen/arch/x86/hvm/svm/intr.c80
-rw-r--r--xen/arch/x86/hvm/svm/svm.c219
-rw-r--r--xen/arch/x86/hvm/svm/vmcb.c360
-rw-r--r--xen/arch/x86/hvm/svm/x86_32/exits.S7
-rw-r--r--xen/arch/x86/hvm/svm/x86_64/exits.S11
-rw-r--r--xen/arch/x86/hvm/vioapic.c726
-rw-r--r--xen/arch/x86/hvm/vlapic.c665
-rw-r--r--xen/arch/x86/hvm/vmx/io.c48
-rw-r--r--xen/arch/x86/hvm/vmx/vmcs.c425
-rw-r--r--xen/arch/x86/hvm/vmx/vmx.c759
-rw-r--r--xen/arch/x86/hvm/vmx/x86_32/exits.S3
-rw-r--r--xen/arch/x86/hvm/vmx/x86_64/exits.S3
-rw-r--r--xen/arch/x86/io_apic.c3
-rw-r--r--xen/arch/x86/irq.c13
-rw-r--r--xen/arch/x86/mm.c33
-rw-r--r--xen/arch/x86/mm/shadow/common.c152
-rw-r--r--xen/arch/x86/mm/shadow/multi.c677
-rw-r--r--xen/arch/x86/mm/shadow/private.h117
-rw-r--r--xen/arch/x86/mm/shadow/types.h96
-rw-r--r--xen/arch/x86/oprofile/xenoprof.c2
-rw-r--r--xen/arch/x86/platform_hypercall.c2
-rw-r--r--xen/arch/x86/setup.c11
-rw-r--r--xen/arch/x86/traps.c110
-rw-r--r--xen/arch/x86/x86_32/asm-offsets.c1
-rw-r--r--xen/arch/x86/x86_32/domain_page.c2
-rw-r--r--xen/arch/x86/x86_32/seg_fixup.c50
-rw-r--r--xen/arch/x86/x86_32/traps.c14
-rw-r--r--xen/arch/x86/x86_64/asm-offsets.c1
-rw-r--r--xen/arch/x86/x86_64/traps.c22
-rw-r--r--xen/arch/x86/x86_emulate.c12
-rw-r--r--xen/common/domain.c93
-rw-r--r--xen/common/domctl.c40
-rw-r--r--xen/common/event_channel.c3
-rw-r--r--xen/common/grant_table.c91
-rw-r--r--xen/common/keyhandler.c4
-rw-r--r--xen/common/lib.c13
-rw-r--r--xen/common/memory.c239
-rw-r--r--xen/common/multicall.c2
-rw-r--r--xen/common/page_alloc.c7
-rw-r--r--xen/common/perfc.c13
-rw-r--r--xen/common/sched_credit.c496
-rw-r--r--xen/common/sched_sedf.c44
-rw-r--r--xen/common/schedule.c25
-rw-r--r--xen/common/trace.c2
-rw-r--r--xen/common/xmalloc.c90
-rw-r--r--xen/drivers/char/console.c260
-rw-r--r--xen/include/asm-ia64/config.h7
-rw-r--r--xen/include/asm-ia64/debugger.h1
-rw-r--r--xen/include/asm-ia64/mm.h2
-rw-r--r--xen/include/asm-ia64/vlsapic.h1
-rw-r--r--xen/include/asm-ia64/vmx.h2
-rw-r--r--xen/include/asm-ia64/vmx_platform.h6
-rw-r--r--xen/include/asm-ia64/vmx_vpd.h1
-rw-r--r--xen/include/asm-powerpc/powerpc64/config.h6
-rw-r--r--xen/include/asm-x86/bitops.h58
-rw-r--r--xen/include/asm-x86/config.h7
-rw-r--r--xen/include/asm-x86/grant_table.h4
-rw-r--r--xen/include/asm-x86/hvm/domain.h12
-rw-r--r--xen/include/asm-x86/hvm/hvm.h31
-rw-r--r--xen/include/asm-x86/hvm/io.h18
-rw-r--r--xen/include/asm-x86/hvm/support.h17
-rw-r--r--xen/include/asm-x86/hvm/svm/vmcb.h20
-rw-r--r--xen/include/asm-x86/hvm/vcpu.h5
-rw-r--r--xen/include/asm-x86/hvm/vioapic.h107
-rw-r--r--xen/include/asm-x86/hvm/vlapic.h114
-rw-r--r--xen/include/asm-x86/hvm/vmx/vmcs.h6
-rw-r--r--xen/include/asm-x86/hvm/vmx/vmx.h146
-rw-r--r--xen/include/asm-x86/hvm/vpic.h39
-rw-r--r--xen/include/asm-x86/hvm/vpt.h1
-rw-r--r--xen/include/asm-x86/mm.h9
-rw-r--r--xen/include/asm-x86/page.h12
-rw-r--r--xen/include/asm-x86/perfc_defn.h3
-rw-r--r--xen/include/asm-x86/processor.h2
-rw-r--r--xen/include/asm-x86/regs.h2
-rw-r--r--xen/include/asm-x86/shadow.h68
-rw-r--r--xen/include/asm-x86/x86_32/page-2level.h3
-rw-r--r--xen/include/asm-x86/x86_32/page-3level.h11
-rw-r--r--xen/include/asm-x86/x86_64/page.h3
-rw-r--r--xen/include/public/COPYING16
-rw-r--r--xen/include/public/acm.h18
-rw-r--r--xen/include/public/acm_ops.h18
-rw-r--r--xen/include/public/arch-ia64.h19
-rw-r--r--xen/include/public/arch-x86_32.h21
-rw-r--r--xen/include/public/arch-x86_64.h27
-rw-r--r--xen/include/public/callback.h18
-rw-r--r--xen/include/public/dom0_ops.h18
-rw-r--r--xen/include/public/domctl.h63
-rw-r--r--xen/include/public/elfnote.h18
-rw-r--r--xen/include/public/event_channel.h18
-rw-r--r--xen/include/public/features.h18
-rw-r--r--xen/include/public/grant_table.h18
-rw-r--r--xen/include/public/hvm/e820.h27
-rw-r--r--xen/include/public/hvm/hvm_info_table.h18
-rw-r--r--xen/include/public/hvm/hvm_op.h25
-rw-r--r--xen/include/public/hvm/ioreq.h48
-rw-r--r--xen/include/public/hvm/params.h42
-rw-r--r--xen/include/public/hvm/vmx_assist.h18
-rw-r--r--xen/include/public/io/blkif.h47
-rw-r--r--xen/include/public/io/console.h18
-rw-r--r--xen/include/public/io/netif.h18
-rw-r--r--xen/include/public/io/pciif.h18
-rw-r--r--xen/include/public/io/ring.h18
-rw-r--r--xen/include/public/io/tpmif.h18
-rw-r--r--xen/include/public/io/xenbus.h18
-rw-r--r--xen/include/public/io/xs_wire.h19
-rw-r--r--xen/include/public/memory.h18
-rw-r--r--xen/include/public/nmi.h18
-rw-r--r--xen/include/public/physdev.h20
-rw-r--r--xen/include/public/platform.h18
-rw-r--r--xen/include/public/sched.h18
-rw-r--r--xen/include/public/sysctl.h18
-rw-r--r--xen/include/public/trace.h18
-rw-r--r--xen/include/public/vcpu.h18
-rw-r--r--xen/include/public/version.h18
-rw-r--r--xen/include/public/xen-compat.h18
-rw-r--r--xen/include/public/xen.h18
-rw-r--r--xen/include/public/xenoprof.h18
-rw-r--r--xen/include/xen/config.h74
-rw-r--r--xen/include/xen/console.h3
-rw-r--r--xen/include/xen/domain.h14
-rw-r--r--xen/include/xen/lib.h4
-rw-r--r--xen/include/xen/sched-if.h6
-rw-r--r--xen/include/xen/sched.h61
-rw-r--r--xen/include/xen/softirq.h5
483 files changed, 45607 insertions, 9731 deletions
diff --git a/.hgignore b/.hgignore
index 62bc580ea1..03756c0381 100644
--- a/.hgignore
+++ b/.hgignore
@@ -98,7 +98,7 @@
^tools/firmware/.*\.bin$
^tools/firmware/.*\.sym$
^tools/firmware/.*bios/.*bios.*\.txt$
-^tools/firmware/acpi/acpigen$
+^tools/firmware/hvmloader/acpi/acpigen$
^tools/firmware/hvmloader/hvmloader$
^tools/firmware/hvmloader/roms\.h$
^tools/firmware/rombios/BIOS-bochs-[^/]*$
@@ -123,6 +123,7 @@
^tools/ioemu/qemu\.1$
^tools/ioemu/qemu\.pod$
^tools/libxc/xen/.*$
+^tools/libxen/test/test_bindings$
^tools/libaio/src/.*\.ol$
^tools/libaio/src/.*\.os$
^tools/misc/cpuperf/cpuperf-perfcntr$
diff --git a/buildconfigs/linux-defconfig_xen0_ia64 b/buildconfigs/linux-defconfig_xen0_ia64
index 400fbdc3c1..0ffbb8274f 100644
--- a/buildconfigs/linux-defconfig_xen0_ia64
+++ b/buildconfigs/linux-defconfig_xen0_ia64
@@ -1529,7 +1529,7 @@ CONFIG_XEN_PRIVCMD=y
CONFIG_XEN_XENBUS_DEV=y
CONFIG_XEN_BACKEND=y
CONFIG_XEN_BLKDEV_BACKEND=y
-# CONFIG_XEN_BLKDEV_TAP is not set
+CONFIG_XEN_BLKDEV_TAP=y
CONFIG_XEN_NETDEV_BACKEND=y
# CONFIG_XEN_NETDEV_PIPELINED_TRANSMITTER is not set
CONFIG_XEN_NETDEV_LOOPBACK=y
diff --git a/buildconfigs/linux-defconfig_xen_ia64 b/buildconfigs/linux-defconfig_xen_ia64
index 085d165ea7..ba209b081d 100644
--- a/buildconfigs/linux-defconfig_xen_ia64
+++ b/buildconfigs/linux-defconfig_xen_ia64
@@ -1535,7 +1535,7 @@ CONFIG_XEN_PRIVCMD=y
CONFIG_XEN_XENBUS_DEV=y
CONFIG_XEN_BACKEND=y
CONFIG_XEN_BLKDEV_BACKEND=y
-# CONFIG_XEN_BLKDEV_TAP is not set
+CONFIG_XEN_BLKDEV_TAP=y
CONFIG_XEN_NETDEV_BACKEND=y
# CONFIG_XEN_NETDEV_PIPELINED_TRANSMITTER is not set
CONFIG_XEN_NETDEV_LOOPBACK=y
diff --git a/config/SunOS.mk b/config/SunOS.mk
index 21568304fc..5d548054d3 100644
--- a/config/SunOS.mk
+++ b/config/SunOS.mk
@@ -21,7 +21,7 @@ LIB64DIR = lib/amd64
SOCKET_LIBS = -lsocket
CURSES_LIBS = -lcurses
SONAME_LDFLAG = -h
-SHLIB_CFLAGS = -static-libgcc -shared
+SHLIB_CFLAGS = -R /usr/sfw/$(LIBDIR) -shared
ifneq ($(debug),y)
# Optimisation flags are overridable
diff --git a/docs/src/user.tex b/docs/src/user.tex
index 6b4e5cfa72..2528861a23 100644
--- a/docs/src/user.tex
+++ b/docs/src/user.tex
@@ -3192,6 +3192,15 @@ editing \path{grub.conf}.
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 [ nmi=xxx ]
Specify what to do with an NMI parity or I/O error. \\
`nmi=fatal': Xen prints a diagnostic and then hangs. \\
@@ -3202,12 +3211,21 @@ editing \path{grub.conf}.
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=xxx ] Set the amount of memory to be allocated to
- domain0. In Xen 3.x the parameter may be specified with a B, K, M or
+\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. In previous versions of Xen, suffixes were not supported
- and the value is always interpreted as kilobytes.
+ 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
diff --git a/docs/xen-api/Makefile b/docs/xen-api/Makefile
new file mode 100644
index 0000000000..7dfb1bfbec
--- /dev/null
+++ b/docs/xen-api/Makefile
@@ -0,0 +1,23 @@
+TEX := $(wildcard *.tex)
+EPS := $(wildcard *.eps)
+EPSDOT := $(patsubst %.dot,%.eps,$(wildcard *.dot))
+
+.PHONY: all
+all: xenapi.pdf xenapi.ps
+
+xenapi.dvi: $(TEX) $(EPS) $(EPSDOT)
+ latex xenapi.tex
+ latex xenapi.tex
+
+%.pdf: %.ps
+ ps2pdf $< $@
+
+%.ps: %.dvi
+ dvips $< -o $@
+
+%.eps: %.dot
+ dot -Tps $< >$@
+
+.PHONY: clean
+clean:
+ rm -f *.pdf *.ps *.dvi *.aux *.log $(EPSDOT)
diff --git a/docs/xen-api/coversheet.tex b/docs/xen-api/coversheet.tex
new file mode 100644
index 0000000000..cd0ad615d6
--- /dev/null
+++ b/docs/xen-api/coversheet.tex
@@ -0,0 +1,50 @@
+%
+% 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.
+%
+
+\pagestyle{empty}
+
+\doctitle{} \hfill \revstring{}
+
+\vspace{1cm}
+
+\begin{center}
+\resizebox{8cm}{!}{\includegraphics{\coversheetlogo}}
+
+\vspace{3cm}
+
+\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}
+
+\vfill
+
+\noindent
+\legalnotice{}
+
+\newpage
+\pagestyle{plain} \ No newline at end of file
diff --git a/docs/xen-api/fdl.tex b/docs/xen-api/fdl.tex
new file mode 100644
index 0000000000..d8214575b3
--- /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 0000000000..0072704646
--- /dev/null
+++ b/docs/xen-api/presentation.tex
@@ -0,0 +1,149 @@
+%
+% 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.
+%
+
+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 groups} of type
+$(\mathit{VMGroup}~\mathit{Ref})~\mathit{Set}$; this field lists
+the VMGroups that a particular VM is part of. Similarly, the VMGroups
+class has a field called {\tt VMs} of type $(\mathit{VM}~{\mathit
+Ref})~\mathit{Set}$ that contains the VMs that are part of a particular
+VMGroup. These two fields are {\em bound together\/}, in the sense that
+adding a new VMGroup to a VM causes the VMs field of the corresponding
+VMGroup 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
+existance 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 to\_XML()}'' RPC that serialises the
+state of all fields as an XML string.
+
+\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 have RPCs for cloning,
+suspending, starting etc. Such additional RPCs are described explicitly
+in the API reference.
diff --git a/docs/xen-api/todo.tex b/docs/xen-api/todo.tex
new file mode 100644
index 0000000000..2778ccce55
--- /dev/null
+++ b/docs/xen-api/todo.tex
@@ -0,0 +1,140 @@
+%
+% 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 asyncrhonous request polling when
+that request fails.
+
+\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 TPM
+\begin{itemize}
+
+\item Would it not be better to have a class TPM and a member TPMs ((TPM ref)
+Set) containing an array of zero or one references to TPMs? I assume that
+an empty array would make it clear that no TPM is associated with the VM
+instead of encoding its existence into TPM/instance or TPM/backend
+somehow. The current members instance and backend could then be moved into
+the TPM class.
+
+\item Also 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 0000000000..23ddb9e9ae
--- /dev/null
+++ b/docs/xen-api/vm-lifecycle.tex
@@ -0,0 +1,24 @@
+%
+% 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{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.
diff --git a/docs/xen-api/vm_lifecycle.dot b/docs/xen-api/vm_lifecycle.dot
new file mode 100644
index 0000000000..fdc97523e2
--- /dev/null
+++ b/docs/xen-api/vm_lifecycle.dot
@@ -0,0 +1,15 @@
+digraph g{
+
+node [shape=box]; "powered down" paused running suspended;
+
+"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"];
+
+} \ 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 0000000000..ecd4ed9416
--- /dev/null
+++ b/docs/xen-api/wire-protocol.tex
@@ -0,0 +1,287 @@
+%
+% 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{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) Host.ListAllVMs()
+\end{verbatim}
+This specifies that the function with name {\tt Host.ListAllVMs} 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 our ``{\tt ref\_}'' types (e.g.\ {\tt ref\_vm} in the above
+ example) map to XML-RPC's {\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}
+ <value><string>destroy</string></value>
+ \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}
+<array>
+ <data>
+ <value><string>CX8</string></value>
+ <value><string>PSE36</string></value>
+ <value><string>FPU</string></value>
+ </data>
+</array>
+ \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}
+<value>
+ <struct>
+ <member>
+ <name>Mike</name>
+ <value><double>2.3</double></value>
+ </member>
+ <member>
+ <name>John</name>
+ <value><double>1.2</double></value>
+ </member>
+ </struct>
+</value>
+ \end{verbatim}
+
+ \item our {\tt Void} type is transmitted as an empty string.
+
+\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 represents an error code;
+the remainder of the array represents error parameters relating to that code.
+\end{itemize}
+
+For example, an XML-RPC return value from the {\tt Host.ListAllVMs} function above
+may look like this:
+\begin{verbatim}
+ <struct>
+ <member>
+ <name>Status</name>
+ <value>Success</value>
+ </member>
+ <member>
+ <name>Value</name>
+ <value>
+ <array>
+ <data>
+ <value>vm-id-1</value>
+ <value>vm-id-2</value>
+ <value>vm-id-3</value>
+ </data>
+ </array>
+ </value>
+ </member>
+ </struct>
+\end{verbatim}
+
+\section{Making XML-RPC Calls}
+
+\subsection{Transport Layer}
+
+We ought to support at least
+\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} is passed to subequent
+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 those on ``Session'' and ``Task'' objects)
+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}.
+
+Each of the methods specified in the API Reference is synchronous.
+However, although not listed explicitly in this document, each
+method call has an asynchronous analogue in the {\tt Async}
+namespace. For example, synchronous call {\tt VM.Install(...)}
+(described in Chapter~\ref{api-reference})
+has an asynchronous counterpart, {\tt
+Async.VM.Install(...)}, 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 asychronous
+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}.
+
+Two special RPC calls are provided to poll the status of
+asynchronous calls:
+\begin{verbatim}
+ Array<task_id> Async.Task.GetAllTasks (session_id s)
+ task_status Async.Task.GetStatus (session_id s, task_id t)
+\end{verbatim}
+
+{\tt Async.Task.GetAllTasks} returns a set of the currently
+executing asynchronous tasks belong to the current user\footnote{
+%
+The current user is determined by the username that was provided
+to {\tt Session.Login}.
+%
+}.
+
+{\tt Async.Task.GetStatus} returns a {\tt task\_status} result.
+This is an XML-RPC struct with three elements:
+\begin{itemize}
+ \item The first element is named {\tt Progress} and contains
+an {\tt Integer} between 0 and 100 representing the estimated percentage of
+the task currently completed.
+ \item The second element is named {\tt ETA} and contains a {\tt DateTime}
+representing the estimated time the task will be complete.
+ \item The third element is named {\tt Result}. If {\tt Progress}
+is not 100 then {\tt Result} contains the empty string. If {\tt Progress}
+{\em is\/} set to 100, then {\tt Result} contains the function's return
+result (as specified in Section~\ref{synchronous-result})\footnote{
+%
+Recall that this itself is a struct potentially containing status, errorcode,
+value fields etc.
+%
+}.
+\end{itemize}
+
+\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 ommitted for brevity; the session token is pointed to by the key {\tt 'Value'} in the returned dictionary)
+
+\begin{verbatim}
+>>> session = xen.Session.do_login_with_password("user", "passwd")['Value']
+\end{verbatim}
+
+When serialised, this call looks like the following:
+
+\begin{verbatim}
+<?xml version='1.0'?>
+<methodCall>
+ <methodName>Session.do_login_with_password</methodName>
+ <params>
+ <param>
+ <value><string>user</string></value>
+ </param>
+ <param>
+ <value><string>passwd</string></value>
+ </param>
+ </params>
+</methodCall>
+\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 = xen.VM.do_list(session)['Value']
+>>> all_vms
+['b7b92d9e-d442-4710-92a5-ab039fd7d89b', '23e1e837-abbf-4675-b077-d4007989b0cc', '2045dbc0-0734-4eea-9cb2-b8218c6b5bf2', '3202ae18-a046-4c32-9fda-e32e9631866e']
+\end{verbatim}
+
+Note the VM references are internally UUIDs. Once a reference to a VM has been acquired a lifecycle operation may be invoked:
+
+\begin{verbatim}
+>>> xen.VM.do_start(session, all_vms[3], False)
+{'Status': 'Failure', 'ErrorDescription': 'Operation not implemented'}
+\end{verbatim}
+
+In this case the {\tt start} message has not been implemented and an error response has been returned. Currently these high-level errors are returned as structured data (rather than as XMLRPC faults), allowing for internationalised errors in future. Finally, here are some examples of using accessors for object fields:
+
+\begin{verbatim}
+>>> xen.VM.getname_label(session, all_vms[3])['Value']
+'SMP'
+>>> xen.VM.getname_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 0000000000..da14fe9b86
--- /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] {<ffffffffffffffff>} 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 <FF> 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 } } { { <BAB431EA07F209EB8C4348311481D9D3F76E3D15246555577D87BC510ED54E 118C39697FA9F6DB58128E60EB8A12FA24D7CDD2FA94D221FA9EC8DA3E5E6A1C 4ACECC8C2D39C54E7C946031DD156C3A6B4A09AD29E1867A> 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> <FFFF> endcodespacerange 1 begincidrange <0000> <FFFF> 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 <d841272cf18f54fc13> 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 <d841272cf18f54fc13> 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> <FFFF> endcodespacerange 1 begincidrange <0000> <FFFF> 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> <FFFF> endcodespacerange 1 begincidrange <0000> <FFFF> 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 <?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?><x:xmpmeta xmlns:x='adobe:ns:meta/' x:xmptk='XMP toolkit 3.0-29, framework 1.6'>
+<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns:iX='http://ns.adobe.com/iX/1.0/'>
+
+ <rdf:Description rdf:about='uuid:bacf4235-e435-11da-8f1a-000d93afebb2'
+ xmlns:pdf='http://ns.adobe.com/pdf/1.3/'>
+ <pdf:Producer>Adobe PDF library 6.66</pdf:Producer>
+ </rdf:Description>
+
+ <rdf:Description rdf:about='uuid:bacf4235-e435-11da-8f1a-000d93afebb2'
+ xmlns:tiff='http://ns.adobe.com/tiff/1.0/'>
+ </rdf:Description>
+
+ <rdf:Description rdf:about='uuid:bacf4235-e435-11da-8f1a-000d93afebb2'
+ xmlns:xap='http://ns.adobe.com/xap/1.0/'
+ xmlns:xapGImg='http://ns.adobe.com/xap/1.0/g/img/'>
+ <xap:CreateDate>2006-05-14T09:34:14-07:00</xap:CreateDate>
+ <xap:ModifyDate>2006-06-26T18:03:19Z</xap:ModifyDate>
+ <xap:CreatorTool>Illustrator</xap:CreatorTool>
+ <xap:MetadataDate>2006-05-14T09:34:14-07:00</xap:MetadataDate>
+ <xap:Thumbnails>
+ <rdf:Alt>
+ <rdf:li rdf:parseType='Resource'>
+ <xapGImg:format>JPEG</xapGImg:format>
+ <xapGImg:width>256</xapGImg:width>
+ <xapGImg:height>112</xapGImg:height>
+ <xapGImg:image>/9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA&#xA;AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK&#xA;DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f&#xA;Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAcAEAAwER&#xA;AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA&#xA;AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB&#xA;UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE&#xA;1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ&#xA;qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy&#xA;obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp&#xA;0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo&#xA;+DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FUn13zZoei&#xA;L/ps49elVto/ilP+x7fNqYq8/wBW/NrVpyyabbpZx9pH/eyfPf4B9xxVIX1Tzpqx5fWLy4Rv5C6x&#xA;/ctExVT/AMJ+Zpvia1Zj4vIgP/DNirY8v+arT4o4JoyNwYXBP/CMcVV7fzf5z0pwj3c4p/uq6Beo&#xA;8P3oJH0YqyvRfzcidli1i19OuxubepX5mM1P3E/LFWfafqVhqNuLmxnS4hP7aGtD4EdQfY4qicVd&#xA;irsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVececvzLMbSafobguKrNfdQ&#xA;D3EXj/rfd44qxDSfLOq6zIbqZmjhkPJrmWrM57lQd2+eKsz03yro1gAVgE0o/wB2zUc19h0H0DFU&#xA;3xV2KuxVZLDFMhjlRZEPVHAYH6DirHdV8jabchnsz9Um6gDeMn3Xt9GKsWjk8w+VtREkbNby/wAw&#xA;+KKVR2PZh8+mKvVPKHnex1+L0nAt9SQVkt67MP5o69R7dvxxVk2KuxV2KuxV2KuxV2KuxV2KuxV2&#xA;KuxV2KuxV2KuxV2KuxV2KuxV2KvNPzI86uHk0PTpONPhvp1O58Ygf+Jfd44qk3lTyksypf6gtYjv&#xA;BbkfaHZm9vAd8VZuAAAAKAbADFW8VdirsVdirsVdiqhe2Nre27W9zGJIn7HqD4g9jirznWdHvvL+&#xA;oxz28jCMNztbldiCu9D/AJQ/HFXq3krzZF5g06slE1CCguohsD4Ovs34YqyPFXYq7FXYq7FXYq7F&#xA;XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FWP+d/MY0PRJJoyPrk/7q1HgxG7/wCxG/zpirxaPyPJ5xsL&#xA;61lvJ7FShKX8DMsq3BBKMCCOQB3YV3G3euKvmfzZf/mb5V1+70LVtb1KK8tG4ki8uODod0kjJYVR&#xA;huMVSj/HPnb/AKmDUv8ApMn/AOa8Vd/jnzt/1MGpf9Jk/wDzXiqc+Wfzh/MPQtYstQXXb+9gtJAz&#xA;6fdXU8tvKh2dHjdmX4gTvSoO43xV9teUPNekea/L1nrulSc7S7TlxP243GzxuOzI2x/piqc4q7FX&#xA;l/5v/nnofkSB7C04aj5mdf3diD8EIYVElwR0FNwg+I+w3xV8m6p+Zn5ganqE9/deYL8T3DF3WK4l&#xA;hjHskcbKiqOwAxVBS+c/OEycJtd1CROvF7udhX5F8VW2/nDzbbOZLbW7+CQiheO6mQ08KqwxVG2/&#xA;nr8xrmeO3t/MOsTXEzBIoY7y6d3djRVVQ5JJPQDFX1b+R/5I+fLc2/mL8wvMGqPOOMtn5e+v3BVC&#xA;N1a7IejH/iobfzV3UKvoPFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXjX5m6u9/5kNoh&#xA;5Q2CiFFHeRqM5+daL9GKsq0LTV07S4LUD4wOUx8Xbdv6YqwP87/yjtvPugetZqkXmTT1LafcGg9R&#xA;ept5G/lb9k/st7E4q+J7u1ubO6mtLqJoLm3dop4ZAVdHQ8WVgehBGKqWKuxV6p+Qf5tP5I8w/UdR&#xA;lP8AhvVHC3incQS7KtwB7dHp1X/VGKvtJZEdBIjBo2HJXBqCDuCDir5+/Oj/AJyRg031/L3kqZZ9&#xA;RFY7vWVo0cJGxSCtVd/F/sjtU9FXy9cXFxc3ElxcyvNcTMZJppGLu7saszMakknqTiqnirsVTPQP&#xA;Lms6/fCz0q2e4lADSsB8EaEgc5H6KtT1Py64q+xf+caPyr8qeXYLq/mhS981QkBr+QVEUUi0426n&#xA;7G4YM/2iPAGmKvfMVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVWSyJFE8rmiRqWY+wFTi&#xA;rwjQ1fVPNEUs27SzPcy18QTIa/M4q9OxV2KvAf8AnJH8mf0vay+c9Ag/3K2qV1a1jG9xCg/vlA6y&#xA;Rgb/AMy+67qvlXFXYq7FWcy/nN57fyJb+S1vTFpsHKNp0qLh7c/Zt2kr/drvsO3w/Z2xVg2KuxV2&#xA;KvRvyp/JTzJ5+uVuFBsPL8b8bjU5F2ah+JIFNOb/AIDv4Yq9180ebvym/KHyzP5T0uH6zqkkf7yz&#xA;gKvcNKRVZbyciinuB1p9leOKvD5v+cgvzEhmnbQ7tNFjnX03FuivIUqDvJKHoajqoXFWKat+YPnz&#xA;V2Lap5i1K85bcZruZ1oewUtxA9gMVSKWaaZzJM7SSHq7ksdvc4qiLPVtVsiDZXk9sVqVMMrx0J60&#xA;4kYqzXy3+ff5u+X5FNl5mvJ4lO8F8/1yMj+Wlx6hUf6pGKvpP8m/+crdJ8131voHmy3j0jW7hlit&#xA;LuEt9TuJWNFSjFmhdifhBYg+INBir6BxV8PfnL+ev5kWn5o+ZLHQ/MN1Y6XY3jWkFrCy8ENsohkp&#xA;Ve8iMT74qwz/AJX9+cn/AFNl9/wS/wDNOKu/5X9+cn/U2X3/AAS/804q9x0z/nKCLyh+Vmix6hcP&#xA;5o8+X8UlzcRySD07dJZn9D6xItaH0uBEaivjxqCVXiXm78//AM2fNE0jXmv3FlbOTSy05mtIVU/s&#xA;0iIdx/rs2KsBuby7u5TLdTyTymtZJWZ23NTuxJ64qmmh+dfOGgypLoutXunslOIt7iSNdhQAqG4k&#xA;U7EYq+qP+cd/+cltR80atD5Q85FH1a4B/RmqoixidkUs0UyLRFfiPhZQAelK0qqzX/nI7XPPnlfy&#xA;dJ5r8r+Ym0pLAxQzacLS1uFnaeZUDmWdJGTiG6Ab4q8488+f/wA7NB8j/l9eab5ra/1vzqUnFdPs&#xA;I+H1m3tmitlHpMrcZJm+OgJrirDde/5yZ/NvUdDsdS8vap9Rg0qxtLbzBMbW0czalM8w5j1IXVfU&#xA;jh5BUoo3xV9ieZJPT8u6o4NCtpOQT4+m1MVeReQEDa3IT+xAxHz5KP44q9DxV2KuxV8k/wDORf5M&#xA;f4dvn816DBTQbyT/AE62jHw2k7nqAOkUhO3ZW26FcVeG4q7FXYq7FW1VmYKoJYmgA3JJxV9Cfk9/&#xA;zjPcagINd88Rtb2JpJbaLUrNKOoa4IoY1/yB8R78e6r0P8+fzQj/AC98tWuheXVjttZv4zHZrEqq&#xA;lpap8JkVAKA/sxilOp/ZoVXx1NNNPM80ztLNKxeSRyWZmY1LMTuST1OKrMVe9/8AOL/5HaZ54u7r&#xA;zJ5jiM3l/TJRBBZVKrc3XEOwcgg+nErKSP2iR2qCq+woPKPlS3sP0fBotjFYU4/VEtoVip4cAvHt&#xA;4Yq+H/8AnKHyP5f8ofmebXQYEtLHULKLUDZRbRwySSSxOiL+yp9HkF6Cu21MVeRYq2CQajYjFX6G&#xA;/k158fW/yY0nzPq8paa1s5l1Kd/tMbFnjeRiepdYuZ+eKvz71XUJ9S1O81Gf+/vZ5LiXv8crl2/E&#xA;4qhcVdir3P8AKr/nFLzV5z0eDXdWv00DSbtRJZBojPczRncSCLlEqI4+yxap68aUJVa/Ob/nGG//&#xA;AC+8tnzJY6wNX0yGSOK8jeD6vLF6p4I4o8iupchT0IqOu+KvDcVdirJfy0kuI/zG8rPblhONXsfT&#xA;4btyNygAA71xV+keraNpGsWL2Gr2NvqNjIVMlpdxJPExU8lJjkDKaEVG2Koebyt5YnTTUn0iylTR&#xA;ih0dXtomFmY+Ppm2BX9zw4Lx4UpQeGKoL/lXf5f/AFOay/wxpP1K5lWe4tvqNt6UkqAhZHThxZ1D&#xA;tRiK7nFUw8xxmTy9qiKKs1pOFHuY2piryHyA4XW5FP7cDAfMMp/hir0PFXYqpXNzbWtvJc3MqQW8&#xA;Kl5ppGCIiqKlmZqAADucVfL/AOdn/ORsesWt55X8ohW0udWgv9VkSpmRtmSBHHwoR+2RU9qdSq+f&#xA;MVdirsVdir6g/wCcXvIfkG60f/FHMal5kt5DHNBOoAsWqeBjjqal1HISn5ChDYq+iMVfCv57eYJt&#xA;b/NTX5XYmOyuG0+BD0RLT90wHzkVm+nFWA4q7FUwtotfjiH1ZLtIW+JfTEgU1HUcdt8VVf8Anaf+&#xA;X7/ktiqhNY65O/Oa3uZXpTk6SMafMjFVn6J1X/ljn/5Fv/TFXfonVf8Aljn/AORb/wBMVfV0dxce&#xA;TP8AnDLjPyivtWtpYEifYkandsCoH/MM5bFXyNirsVZT+V3ldPNX5h+X9AkUvb315Gt0o6m3Q+pP&#xA;0/4qRsVfpTHHHFGscahI0AVEUAKqgUAAHQDFXhn/ADmLr66f+VKaYG/e6zfwQlP+K4K3DH6HjT78&#xA;VfEGKuxV6n/zjJ5dGt/nNoQdOcGmmXUZvb6uhMR/5HGPFX6A4q7FXYqtljSSN43FUcFWHsRQ4q8H&#xA;0Vn0rzRHFNsYZ2tpa7dSYz9x3xV6diqSeb/Ofl3yjo8mra7drbWybRp1klftHEnV2P8AadsVfHn5&#xA;s/nh5i8+XD2kZbTvLiNWHTUbeTiaq9ww+23fj9le2+5Vea4q7FXYq7FXYqy38svzD1XyJ5ng1izr&#xA;JbNSLUbKtFngJ+Jf9ZeqHsfauKvuvy/r2leYNGtNZ0qcXFhexiWCUeB2KsOzKdmHY7Yq+Ffza0W5&#xA;0b8yvMdlOhSt/PPDy7w3DmaJveqOMVYjirsVfpZ+WGu6Prf5f6BfaRMktmbG3iohH7t4olR4mA+y&#xA;0bDiRiq38x/zI8teQPLs2s61MAQCtnZKw9e5l7RxKf8Ahm6KNzirxT/od7yt/wBS1ff8jocVd/0O&#xA;95W/6lq+/wCR0OKp15N/5yy0vzb5n0/y9pXla+e81CZYlb1oisa9Xlen7MaAs3sMVY//AM5u+YvS&#xA;0Ty35cjfe7uJr+dB2W3QRR19mM7/AHYq+RsVdir3z/nDXy7+kPzNutYkSsWi2Ejxv/LPcsIUH0xG&#xA;XFX2xir48/5zZ8xfWfN2g+X0eqabZvdyqOgku5OND7hLcH6cVfN2KuxV9R/84Q+XeV75m8ySJ/dR&#xA;wadbSePqMZph9HpxYq+scVdirsVdirxz8z9Iax8xG8RaQ36iVSOgkWiuP1N9OKpD55/Pzy35S0CF&#xA;mIv/ADHNHSPS42oVcbepOwr6aHqO57eIVfJfnPzx5l846w+q69dm4mNRDCPhhhQmvpwp0VfxPUkn&#xA;fFUgxV2Kro43kdY41LyOQqIoqSTsAAMVe5+Vf+cWPMeo+UrzU9XnOna1LBz0fSjSvMfEBdE/Y5jb&#xA;iN1rVuhXFXh91bXFrcy2tzG0NxA7RTROKMjoeLKwPQgihxVSxV2KvX/+cffzgbybrP6F1eanlnU5&#xA;BzdjtaztRRMP8hthJ9/bdV77+cP5LaR+YdnFeW8qWXmC2Tja39OUckf2hFNx3K1NVYbr79MVfJ3m&#xA;78r/ADv5TvHttX02QBRyFzB+/hZakBuaV41p0ah9sVYpiqYaV5g17SC50nUrrTzJ/eG1nkh5U/m9&#xA;Nlriqhf6lqOo3LXWoXU15cts09xI0shHuzknFXWGmajqM4t9PtZry4b7MNvG0rmv+SgJxV6n5L/5&#xA;xe/NnzJLG1zpv6BsWoXutTrC4HWgtxWavzUD3xV9ZflH+R3lL8trNmsA19rdwnC81ecASMuxMcSi&#xA;ojjqK8QST3JoMVfNX/OV6+Yte/NqeK0027uLPSbS3s4ZYoJXjYspuHIZVofin4n5Yq8b/wAJ+af+&#xA;rNff9I03/NOKu/wn5p/6s19/0jTf804q+vP+cNvKN5o/kvWtVv7WS0u9UvliEcyMjmG0j+BuLAH7&#xA;c0mKvoPFXwB+fcPmbzH+bnmTUIdLvZbWO5+p2zrbyshjtFEAZCFoVYxlh88Vef8A+E/NP/Vmvv8A&#xA;pGm/5pxV3+E/NP8A1Zr7/pGm/wCacVfcP/OLHlW48v8A5RWRu4Gt73VLi4vriKRSjrVvRj5A77xw&#xA;qfpxV69irsVdirRIUEk0A3JPQDFXy1/zkV/zkboMsLeWPKBS/voJKz64pDQQsAVZLc7iVt92+yO3&#xA;Lsq+Vbi4nuJnnuJGmnlJaSVyWZmPUkncnFVPFXYqmXl3y5rfmPVoNJ0W0e8v7g0SKMdB3ZidlVe7&#xA;HYYq+vfyf/IPRPJMceqapw1LzMwr9YIrDbVG6wA/teMh38Kb1Ves4q+Z/wDnKP8AKoRv/jvSIaI5&#xA;WPXYUHRjRY7mg8dkf3oe5OKvm/FXYq7FX0z/AM48fnhENL/wj5jmJuLKMnRLhjUyxqP95Sf5k/YP&#xA;8u3YVVeh6Tp995p8xiNiazt6lxIOkcS9afIbLir2C78n+U7yOOO90axu0iVUQXFtFLRVFFHxq3QY&#xA;qln/ACqf8rP+pN0P/uG2n/VPFVe1/LX8ubRuVr5V0e3aoblFYWqGo6H4Yx0xVPrWztLSIQ2sEdvC&#xA;OkcSqijanRQB2xVWxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2Kvmj877n/nIfzqZ9C8ueV7zSvKx&#xA;qkp9e2W5vF6H1Ss3wRn/AH2Dv+0T0Crw3/oW/wDO3/qVp/8Akdbf9VcVUrn/AJx3/Oi2geebyvcC&#xA;KMFnIkt2IA6miyE4qki/lZ5+Zgo0h6nYVkhA+8virJvLv/OOH5o6rqdvbXenDS7KT4ptQnkieNE6&#xA;1CxuzOT+yB18QN8VfVn5e/lr5Y8iaT9R0eCs8gBvL+QAzzsO7t2UfsqNh86nFWV4q7FVG9s7W9s5&#xA;7O7iWe1uY2hnhcVV43BVlYeBBpir4686/wDOOHn/AE7zLe23l7S5NT0XnzsLpZIQfSfcI4d1PJPs&#xA;k03698VSP/lQP5v/APUtzf8AI23/AOqmKqF5+R/5qWcPrXWgSQx9OTTW+58APU3xVD6f+VX5lyXs&#xA;C2Gjzm85g2/pPHz5g1BWj9uuKvt78jtO8wWnk5T5m0eTSvMfMx35laJxME/u5IzEzgIVO4P7Ve1M&#xA;VeiYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq8885/lqLl5NQ0RQkzVaay&#xA;6Kx6kx9gf8npirCtL8xavocxtZUZoozSS0mqpU+1d1xVmWm+bdGvgF9b6vMesc3w7+zfZP34qnII&#xA;IBBqD0IxVvFXYqpz3FvbxmSeRYox1Z2Cj7zirG9V892FuCliv1qX+c1WMH9Z+j78VYzb23mLzVqP&#xA;GNWuJB1b7MUSnxPRR+J98Ver+UvJdh5fg57XGoSCktyR0H8sfgv68VZHirsVdirsVdirsVdirsVd&#xA;irsVdirsVdirsVdirsVdirsVdirsVdirsVdiqVa55Y0XWo+N/bhpAKJOnwyr8mH6jtirANW/KPUY&#xA;mL6XdJcx9opv3cg9qiqn8MVY8+geddKJC2t5CB1MHJ0++IsuKqf6e82RfA004I7MlT+K1xVsan5w&#xA;u/hje7kJ2pEjA/8ACAYqiLXyP5y1KQPJaSpXrLdNwI+Yc8/wxVlujflHaRFZNXuTcMNzbwVRPpc/&#xA;EfoAxVndjYWVjbrbWcKQQL0RBQfM+J98VRGKuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2K&#xA;uxV2KuxV/9k=</xapGImg:image>
+ </rdf:li>
+ </rdf:Alt>
+ </xap:Thumbnails>
+ </rdf:Description>
+
+ <rdf:Description rdf:about='uuid:bacf4235-e435-11da-8f1a-000d93afebb2'
+ xmlns:xapMM='http://ns.adobe.com/xap/1.0/mm/'>
+ <xapMM:DocumentID>uuid:65ad4e0e-e367-11da-8f1a-000d93afebb2</xapMM:DocumentID>
+ </rdf:Description>
+
+ <rdf:Description rdf:about='uuid:bacf4235-e435-11da-8f1a-000d93afebb2'
+ xmlns:dc='http://purl.org/dc/elements/1.1/'>
+ <dc:format>application/postscript</dc:format>
+ </rdf:Description>
+
+</rdf:RDF>
+</x:xmpmeta>
+ <?xpacket end='w'?> % &&end XMP packet marker&& [{ai_metadata_stream_123} <</Type /Metadata /Subtype /XML>> /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&kFY<FlrXS_PAtmN]Ljbu=A<r@?WQR@K]8]PA.h1lL)d%f3@Yb(hW%(ZHVk:<,+8>9G%;<$r+h\4JAk%%?HMjl%q;R=s %l1<s4qoA0&VuQP2<E3""F^&*.AUi5eNVh^ns)J(n]8u26fCdMAGi<dF<t/Ej?+8'<^b,5%hc=^Nr:KFKLV(T&?TgWe]DMACqt0CM %r:(S)e&1Rb5F\kN:QM.+j'ViUI"2%mkJ)HAE;O=_rVH-[h`ooR^!C#mq.9HJrr1kos7H6Jp=!#k?/TAg2tX'+J%YaJmMAss2h1bK %a*/&dhqrhc%Cgcbb7F+88,T+]LAq2KmaEQI+$??PkAPkKYQ)rBo>aX&+/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`n<Qa/6d^78]-Fnh6VR+2818/Qut*?2&KkJ?SC4n,IG.pudFE:if>g=(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<jfXGFsCN4?\)3hYuML.-.GdI/`W9gE5jT5>#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`$j<S(OTO1YG%RXY$bUnrTC?/s86_k5tF5IXkq['&qC %?/rD:NSi;EJ%OUuh0`;UiDj0=n%'"4l8lSXlh5\-Eua_=EPuW^Gi@K=Lf]Q\?A3mf:V:GS`N-FO]Q:^(Dmil_B"uB)p\V,2]D$[M %I_Y&m\Ad60D@\so^Vr;-qmprXi(pumqh.3(.dWAmPeY*WiD7)jH\=Vgh`;Bt0D5^%i]HK=rkt7MS@E1t&cT^tj)=i0qh3;VdH'KN %<S/u&M:_+gpmKD<[[bC*8!-:JJ+B/cF)F.MS_M.[a-Q$2knX-5X0@*K'5QSFjBhG%4Ce.*YJU;'%.gFe^">RIM=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<D.hYMZS=q&]9a %maeVKFI^5%kLH^#5--u]400U]l!]m#Pf#UkMf^67mP#R5<c,$(Qg<`pbHdcWrar04b,_Q;S+)nS9uHG;Dgo5Mg6Or0^\QFSr'(&@ %f.c^k@F/Wto%IpTS+)nSe&:^mDgo5Mk*\F?^\P:lnb_gF?\Nta4ib+NT00=T?Jk9kk[2kVRs%#_0Qrpo1\C9Nf[%p8);jiE*j4km %IVu4U?f0o"p_R2mo^dP"2HYaJ#/6MB'jP8#nl>)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<Kn#]gMb03<B=e1g*FnSD(i:^?]DXZ^cU\VFs?LLM94cM8rlA[tS+ %5<bG&VmSEA@qYmM6S!PF=6/(c[H%3B@ro!83d&aC8RX10q9luMb9u'=n-m"bY(A?B]NQ4@ajP1kq8N'^)Kj8kY0fk%q_qT!pq,nQ %Ib>`6K\?*\T80i74780<NRnSuV#5FDJCmPrVjN]$BFb/UfmASnm]8s76K?GVN9;UO[Z"BiE1cqFS2"k2f&fJ10"-,=f!i3ll!#Vs %C=j#2qnQtt>rS0o]%:*CF\Ya$p-S`>fb:PG?$5k_pbtAOT8gV[IV]Km31B;=<XVb0qIJC)(s<heG[,M^FLTGp%fAF/o=^QS_rh=h %AI<S=I.ME(Pk;oEnY>Do\rQ9nV-3HWW5!WCBD1H&j/mE_Aok<A=m]a4nG@W9`Br+b*VdaJ:Of6PLT%H&Vm`3f!;f:bmU_pe'5>0n %>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=;;b5Kj<L[0)jQBaIG3kl$lr3lMh)#GZ(TD_N#jtXie %WLhh8R"6HGL3M"Qi+Yf-e$Db5rN<GAT5+qsAbhXJhM%LX0'JJO8(nL9REA^ZB2=GQUKd4?Eosc\?UMD7W(oJ//_6TTc('qp(0W<% %GYdnClA<j"&[tfOeaT(*9m;A\B^?K3P?Y:0/?`I5>2V)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<j6up'oZc!3T0@bnro!)3k2/?eICf<#s7kEfh::06I.r$cmrrI>\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_f<HpYYr6e'4fRg\5n6DP-rV_enn(]7,/(o`":5S'CB`bE!1C+91F"8)HOY %rqPIOnT232J,aK^Gk9m8qQX70Vn\jqhg]tX<S]mriSP_p5JI$mJ8b(3>PMHprbnFt5%5ns5Q0^6k3bIj4ktLZ5@J]j^UnoE)uO"! %5!6[B/t;CeB:.tr%o8f=m_8W6hXO.l4ZqMLO$3O<rU%5/Rgi057M_Kkg>+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:<ejGU`/okD1Uj=>]*=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_]RTe<h=#gf %rm(J9lq5sW=(Par^pr1ugZR;rGgkL(5CO";imSEM\)CH9"[FX-DK0jiN@Tbs2]Z`2:\V=/n*e$ip'*SNJQr:p^41TXs7m[sKR9Zf %',!7&IMd";r,DDdm?K3Bp')oFDP4%Nlu-dVLE$4[=tpLGoA.<^gT.>jAC\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\`</mBM%!/r"(>9T7jt?XU(;m#GAXMI5Up\2!e<< %s/mFbY&(iLp?DC*R9KH/p\=3$%o;)P@J$/lD7o`&\'Ns8<K!u3Xnj).jm17eTC0Qa5CN;JIG1j<rmU9/q!m?9hB>-(m/HeUcOXi^ %Wk4]@c1h6sRhmL10<Y:_^AA2cr9AdIdJ'8/O$@^M#NY,)]a*]c++aBj2c(hLgNnU[K<fD&-mb)b8dtr2/X-`;%qTED-lXQJPV!R\ %Wg[5"F7<\sm+VS6#mFmq)G3AoduI$2AUlO6fg(suCoPWTG#\0;]#&SY=&Q#je5W@a<#<;q.K^I\:XH(\7qKFanGfB@<)@=C/cNcq %G+5)GYSj-j0m9mW12h?*4oV&\.-4FeIg;N\l$ir7T*?_([%[NK1f)k?2smOA[b-mB.MrZ^9=O;^$rH9"S[R;?DEDc6b>?EZK9<<E %`3?l04No%7c+KSp2"<ocmttBWV8Cp<2:*dqKS."T8G#^_-t<Qh/"qO`e/?E3gm@o<W(Jg]f._D#NYtCF6T%3`<)Wt&X6+.umH^K! %l`.*n?,HfS0W][cct_L(YbMq0dG22flN>E,6urg$9U!X5Mc2[b1r9AVS2Hb<L*ikf`bkn=[dA7o^.79r%4eAGK/lWA6k`%b>LQsr %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"g1<B56X.fn%obMfXWE:=/#5Q>mH6DdaW#(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%3<kkXe.kZ-P*\5U:k3@kCVIuF'4jEASL_,cFM.V$#1HcAulD\k^k;_c:c$W6=%LHGR %VVTY4Di$mQh!==mr3o'rGn%]m+KPZJ#u&uT>3d31q4<$FoUgVnZlAZ#LAooR0!RWlB[P\1T+6>.<l1JMk3"AgF-lgsms$5KVVRQ: %$rK:BKj/[hg1.t\8jlIs*Y:a_H:[OmfJ3'fWBe.7\"U+`9iO$JN92K(^1L#l#UqdSm'5K#k&%*p2VkdP8OqI,]KId5Pb$]tUMNMl %p85!_CrRK47c'"ID8i*T+`G;@)pYf"AL#>]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[_b<Y(U9a3]_>s1oGe&+'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"<l51gQc<%C'r00I$@>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'(o<k#f!B)-Uq$Yi %Q;1.MLUU%'Q4EU/*qD'<4=Q967d'\[XRkmhcr8daN44a'C`=8Sg'fTpkse.&`^b9cMV.E!fP`*<)^&4:@(?HcZ;bTD7P%'=OE+&f %\VP3i9Xnb>jJIP&Cdkhms-hM999jJ\V6WCe*/PKe&5gBg\]i,Cm\K46H2,<ESm+dcW<E`NR4XQJ:<m31I7O<YF@99lUK5]B4Y&r^ %U&Q=3_#KRrE(N`@:"1$EET;lLm6kdTL`7Ck6jMZS3T[@Z?tSDjpN.Ur:ZSHCQmn-7G.@!tAg>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@<W6u-Ta&Y%B^&.MW!<:MXJLajUcDIFtkjhr8j %WiW/IU6c[!@K\`b,%D0&6#:Wg2^4M9kqH3';<:<5+B=.);P_Ht.S5dSBr?Y4Kr7<dKVh*`K;V$`Ju8Vr_9;n?s57ScFms\?H/Nht %H-g9HA\</mH)u:e]a4%46O("D\;Onenkpo!#iY1c.K":JhOcAFGKbMj-W1WJ/.^h&cSOAiMd";O=2^%4Ib(dccuqq(TBK\DkoW;l %ePTs_ic],&;FMM,K!Yoj$p&YOFEBtT[/t"cIX2#o2%Q`#W:M3H,(qSe:a:dR;IZ<UG?tb'h1R"*L"$cB6(occ/]gJ^W)-KJ.P"\s %Suf.-=fEkp5'^6ZrM"Cl@S[+e(RuHS\[&:X.Ug/K(JPGB46epYAI;;UW!oU;TZVa4OKe:e'YR0*8B.Srjg#P.@M08^0VX3`<N$YN %rtN<TSb'IPI[=5CpsTq(6`::"Y->u_a7k:kZag>gVXoqU40Q057n6mep0d!#"ZrOmeLrC92Pg,=JJt<C[tK`;PLd2F&i5WpG9Z_# %NqC#`!,5j0GIu]4eK7AVYpC\#W\6u/"]-HG.YB@l^b__5RC^JA5l0NMJV],1,t.sH?Gu/91g.N=Uu$k]8HGjSM"HP,SB0YWgcLj* %MC<7N?%`Zf*8Zj(Mj_#h\A_[\Mmp+;m.B(^Drq">s7^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/l<Z@P`q2H]i>k:*`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&J<ShgJIK,=ejLJim`%-_GQ=XNIXilh\S %]C8]%<T/kG4NNWMT^0Dj_09Jq3ap`b*l;1OCi%=ckcAT!rL-YJ=^^Ooi/6itkR$cqCrub>f*iC'gslhf<rbNc:qZ(n/ZsV/@Kg,N %0>>!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%<QUM5F&WQB$M;CW %dZfnBHQ)*.?rboB;))n>n=j73auHY]12GW*Z&&R`7#Vba8Y*(V`#gQOP+D,6/r;/HV?g<KA;H<fWdkWF`>rM_*"_[*aSdg]_jh8/ %4nY[pjHhTa(WMJ[*ThI*D.##:jDgR)$2b6R2]29q_h$>OQWQ6(I;%itXZA4EA41Pf8>^$f:u/-fHC?jQK[ZSLM,d27Z'80:<Q95k %mhb%c4W%G"8d^HOS.]OK4;sO>,LIJk;&sr$;-%D<0m\bS[]$Jm%oQ%2hKs/V\%U5bmLQQL3mpBn;T.EDKS].D+[Z,#'.ICaHO_6Q %$4nUL*-XUVZ:!_AHoOWHYgPn#*@13mR8&C:22>E"VCIP<D3,$]XY=knF#QTBB0P1SE.4Vmc!^7*jmP'be'cI)9"$5,Nj8L&9;k." %9kE%\=F__Ocj(`O'^,MOaoD%>2: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><kJZ!VPL]CQcd %LD^)9Z?#e0#/He*SkbR"]L,bFJl:CqQ[HV[pu@SrJ@4?:pk-Z/'_)Hp9?:5&>\%<tX`+ZC."SZD6Yp+MAD#RtU4QB6lnGsn[1k\s %p7ep'f?&a]>*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=^]dN<m7`L"%^?^FDor03+B.V9-@(T,651#V<q&TRd2##k=8pREI"roWW%=22>Yr>+Hb %s!Q=WV+=SuP:ELFBdgo5Nl($t`RQ>THa2?)5d%GtN1I8&Zpto[H#HIIdDW6@s6P_Ss3LQ!q*2OLeWTgaO[rJt9eksj<Ca^%+l;UW %W[pLKaD'a43+T'J--oJ.0V;6g9BRn'2mbcCp6OPSMXN[TVif)`KdB<HahcR*<@]BbFMK=CT\<0MONX"?oB1X&1SkhJHoskEL5.+= %*X9H,f0G7r8.r65<`%b*$Bcp07"<tdD\+[KREe!2ap(Z95$hu]`3=hQ`?9o7+QfG<>S;_-+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't<tW(Q<,<HPYOCr4bK %gk:XtarR;M!Wphb64H4Xcp/@P)O6&`i"C%:9(WP=\/&7$G0W/$(GcWO&^KnIApnK!36#qHVI?rJVKn(4g.701$]O!"$]`dc%uTuP %=#OcWE"R70&*/^7LkCVX3r.kW+/ucg10YV@8=LG^Jf/)W$LpM)m``"82bPe+B1m%dp3%9cg+Xf7hrCa3XFKE]9I)rD)P4I4RTnF9 %Ham_k1oc9`AtWuu>eL<lXs8M-!4d:tQ56%jOEmDEh%(o@L\tFL1?f5.'\UBa*;gAZ*%<.^e-e\rrJoV,1LIO:C,1@2i+*K(I0hRs %;m[je?kV+&ODpI`%R_haG_l`-KI[WZ9*htZ'.1<MJ16P&?r1/tY7e&K'^2Ak5Q_O/Y[\\nn-KK*;'o>;2#d77I6S$GJV"U>^\bnQ %No$=<j#FkRb,tNm?g&s#c2u`[_,JLI"1N9-Vj(DH29J?^ke)V;gD[6PL5U/&"p;dH1Aa6j`(!M?ck7D7^nV]Z`.neCQ?dTo[@m/U %)l?[a3'o.bL_)SNK&@ER^af[hY*8:]++E3llIAUH6nV&.$[*SA+BF'W1=1^dQ9si4f@FF9N6[?`ZXT1koRBTXC+f^ni'X:G&IE-+ %`7;d;&R-=B23bS#nJ2+,`WReYOZ<KaS&^Ko88jJ-l\b?Nd,"gNa4=X:GiT=";Op=)M\4iKCQ^S%_O51Mdp%bkI@`WZT2H".W1Bgg %G$-4NfL\?ab-I+B3/_?F3PQuRBMltp@Ublh`(!QH$k=]'CjZRNodQR(ckA;&NY1'aXOfmX]Ij8t+iQ$`!g`+o_d)8S:*N/[S4_AL %(3c@dd6NO7(U[X&ghu3gr7#6,7]bCt71'M^=/p6q&3V1`/qM[0)</+.IE3);dN3*TH`$=649W**%lJ^0m@>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&VY<K(nfOs^]*-H$1 %$%Dt<YF#8=#c>IH/`L1*d1%R<Ud>H2Y]#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]<K %g'F`+g$l=&EELSIf'b-:8Pu:q3-T;9/N*mIL8pa/7,c>tG4M+t2P0NGS5DuuQ,D0k6f!X2)0J<hQINn5r$i#bPP;!Cd5kGN!BkDm %gQ`S/Cg(K!4MC2g]GL^-?!%B/?NrS:g.?NbJ@ApY'ocQ,@:.%+1INa6X<P8RTNuph4*X7c9aSka$jR1^/hI=q_4e02irk[3U\.U7 %HK*Jna%'u[1/m7u*iILiBX'Y<'nWCS*)Yb4kUq_3WC=ZAXl\Kk<*mfCFErmgQ+@s0`P=;T/;7'Qc_7mQg(;^:0Bp*D8<Pu1^Q:mW %[(t_5@b?m!3<bM]YS7FI$B\ae6#:M4V+."XlYCXWOZ`rCU30gG@,*Y#jFY6Z!Jqao=gubDCa,C;LpWU*Zl:HgX"K#2=\%20,p6_. %&ot91IHs1ArJF\03k0'ZG'c:<mH"gYN1h0f7^mZJap?`p\]RNF"An(\QR%AgK)gs+*bo6KKe+a#>N%Z"hcn(m>?[+']h]5]K,>Zr %`Zg3X\F@dhaLrE@g`hG&Wa72jS)0q'9qo:g"&QKP*DY^k_E.2n++,'\<n',kM>\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\bc<KQZg,'&UP/)X+'6S8f<X3A>2R/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<jNuAn--+"(1IY=-uJs@#)]h$2ie']Y_Ri %Kkf7/dE8^SH>+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\<r(l9Q=>87=qfJ(Lkd@/*<0GA8UJt0%`_,2`);[BFadF09b%_7'c %5ReOkONGJ_pBf2<^uEut$<=#*o^!]tgJ<AU7QNsV6%+YYOVHXs8lOacoKHApS)aU/^(F\XpAtk5I%<'+=IVB>rs%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&-<cG2;X?onZ?4hWX/>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/*<T1rq+T.lGi:-l28Tk#W93cB9he?!qrY %n5UrK*HdrZaU$LdeWb&lG*/S*-E:Pp<8Anti"`Ul(S%0DaNGn%jdbfc/&`P.qIj"8nBYVD^FJhtiTk02JW"mh1Z-,E'Jk_!nd3AU %a)SXjT])7!3(3](&Xp+O4VL[6'l;RSQ/kE4<0W$(Mm=YJ<TLc7YS(/mK"'LpH't?S?VLlu:A(EhS6!MsL't\+/t5BqF_.@V(.'ad %7'a-e6:o*G&Vph4%,=;4iBWKU>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]/SWa<I*P)h1j[m6BB()fPph3E_1RhW(UfFc.'c4t+ %7CcJc<?#U:!OSLg-hn:0jVGr1*[jho`+]47\d)-a")5Zj<mk4g*dcWC.4d@E(>aB=n1Atnc4oGQTUq1qM@?LX?<[q:$^SGZo43Rh %@fiC]"#[(9e=%EMX9[H[_9'(ic?"^BA-Ual86</C7bR3XO/6j$;[ji9gn$o8CUjAc)>1DkYdL_lM$4leJoN5s,>lRf5/Bo/5kQTL %N$XS3!m]AfK+i3UDN&Q4%57eb*)de9$[6R*FE$/O7q+UN/=a3Ok97B*@bh<c\:asB3Ns[t9G-5fdeb.ik4i;<5Md%s`kYr8&k>;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+gt<N^Vu.Z-?bR(>3#+*: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+ppX8E83k<bH-SQ<%oAF&RL%NCQ1PM((X( %OBW0k<DP0p@]Gk@!cN$I=!?9)NS/Z`C=&R;/h?g+Q@4okFglCm#P+#gS33Up9=$Q0FC^0M;M/R+V!VWn6\<3cl:@t9bH5H!^WSN` %XfFrLNP+jPO8+$`Xs@#4\21K<p/G9k9&e.CPoRq_kem2N$/1Q8.liiaKN'81S(>EP[!`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$ %<D=3$h-p:e%PQo@"-P!MPl-'%\rXVt!J2Z=i)t9We:s.S@'%Mr!'Sg0HA@2b53jgm+P9Wd6V9BV<-B8N.%3EMOjbp!AL+0jjBjbq %.CiLOK:U>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!%<N2j:!_;!:U7**bPR5]+c!V(:]2Y4R#%j/n-N2k8^A+96"*9RQ"ip=\\2rE5Y2L/VH.$@"jrE %aU*oJD6[B;#ph_Em2EcGpB`O1#:4@5pU0SB4Q8S/>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`h<j]-q0'89B'\$JOV4)N'bJWQ>8KJ2$IQ.2f$/j+Ls+Nm-Z1j%O"9&nNV]WM!lb %O'HoXR,\T4Zs2ghnbcj%e18O4TOQ$F,WWD;1$\<A'5rm1)`Bl*Gb1,#8-,O@Url:P\p'=k\Kj4?'@#V^M%Y-R>2n3',33ZG!>t"b %f!C$47ui*=*@_Fs<MYO#"b.[?XQj^qhpW;]@/qCZKl_usaK_l25u.6P#Y(I5D*C&.S`mY-_I!8a=6J1n$+Hihi>+`PDjC>OEkK5K %G6rNl`2j8@QR4!f%c0+5figQqS&qc(c9Z!eBM@'8%liOq;Y>?4YnrQRg:E_W<P)>@)dU)%Git#eP0%U]F@3@4,Uu0^-IZ4?BstaW %[P0W;G_/9.nd/QeN'h$<X]F8hoIDYf,qD0<s+R*,Qi<gTj;JQn/n#s7o+uG#CCHoKKHo!4>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#Pm&#2a%^ %D$L%&1MH8rHH@cSiS<&r3CK_(&&o4o=_r\jj9rQ1Q\8B!Fl0[9WAaaGnS'[RV<b:H_mo!OCHQbL2GNUD]!&6GZF7C5S$EF2UgJ)d %0R7ch?GkilX4&`3\'k5d]cLAsEYEptf/?SoIgtT1GZCS'c@3sQ3)WgX?Pdk@Rs:F`$b@6bK!+C]L:)\qc3[\MrSur*LV*:^hoSAQ %F3BWK?B&0?]j.VPS@J;Wa_@_W/b_H,V.R,OHreEcK&F[`j-%?HSD(7O*j"qC/N$+.927C6jg$,?)pWT*fe,5IOYcg?QWc`PbJL0m %XCP6e4qT-rD?iaQk^h5("5/Q!@[R?GW_#.FqH..WVI^'b5:,b"(.JcWA)i!-jLn2Qp6XF0LWaE?:Wq7qL*14n[h;36//\@iriU'W %qs#PlOm#<8E7IO#%G,XF!Aa=:%7&VukA8s@e0tj:L5J$kUFkeL:t"#p_A"O`Fd-$t?gLhJGqhg[C9b"Tdk$3cTR=6DM7%S#29lTX %XXHiLkdb;c4`_HXU(WL2aU+]fWF\7o>g41X/?Ebpc@XaJlU0sO<VTpr_Z/?>nVdf.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\!<j:u!&k@,@tb;k>ZBt)C)9p` %/=]6Ol!JH@!'q9n/c`]o5huEL7fZN4&BqNX"/psO;PsMuW<p;3_)XdJs+@9@=iK8Z0u,`bfr7%u.I1d<h#_a`X411/o93j4e5:`i %ohH4KC97apn]"GDL0"Q/hT7Sg6T2p4+@0M[-4on#mU1&%/k2d#0)[&0\C6V^=W-169kMSkGF=-0CU3?l11u\H2erK]7V(>F^`%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<m4ViPHK1p2Y>_ %WJtId2S]&d)[[V</tCIt[J5-@.*pR9(o)$QLN&a:*594_l+V3cp1B<p><ZFR71)\sN/rh@:Zi6`H[&kE1B*hapj.h4[d<WeiEM#u %'d&PgH6&dWi=NSq;NYDRF!Zs`E^En[_$(Unb'UBi?j$S@.N(YFJWs,!fIk*pNEaI/3Nu(JaGpN0cH$/>ZGDCGIJMpPU7ZmgQYD,5 %%'2N-_1,^,Z3]<JZRi3Z/0'CEYj-(V<LB+VPb:<F;4tkM'<a647UHcY5Q]Yo"^]i<?G,bK5VWC/irk.2(#D=Vl3F(9=$JKpjk+hp %T`@l*!3.!A-P+r$0le0fc=aul/92demR,8bq';@$`pYa<[#^?+,$Z<.66$[Fooua3AYnsbo59DY`"H?`+0GIm7P7@Ba2Kq!`RoSn %Ed3MTRuEu/1G3QZY)hL^W33HF>GOP5`?GkiEKT/Fi58DM``[KJj.lbMlS;LuUSTk$$CKQ1!iEm9CsB_.qPC,oM5r4\l.8dOXhr"o %N7!uECc0Ep"PVR+>8qCj#e+(DgocFf/i/pjX?^Yd/^T=3PLiet<Wj-D1_4RY'g6T*)<f$)fpGQLEY*bAXQ6\$CF)\M2."m/5?p(` %p!@oBZ1jl3Qag'9DGq(4e4&FtE;D.>EB8WDaS6e6Vsr2*>n;se:<*j;jQLU*l'$bQcJa%%Z$iFXjF]>NUQ`k^Gg^np++C#>LJ&-* %.,jNQUS$^>Dmd*__*+i(3eiu)btd2ZcETGO.;Li@Mp$VA$?Ol]cRpXp<N^RM'<eb(Ej4r:`$sP4@AOX`lf(SPL:aQ3#EHl4?"!L\ %8=[^jF6c)"L+,hQV,TYJe%2UopZZ.JFm<?'<S'Kkd0EMhMJJrb-KcVR[YpfU&@H7?YidFA.ZQG03+g9"r^WC.;s]VdqFCg?`+:)_ %1dd3Gko[gj2[C\s!!J-Ie0brnO<F@;VEKcC.qT$qp;Nli,o%KMn%;CU<oM^L^0d<O>]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&*<HqBZk3mpBaS49"#.u1C,haMn'o!8##*%-&Y\"5(taPjBD1%#9hA:ZPL%/G7`jDZ^b"ZDZ>>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"N<uR3 %VU!Oe92V+WSg``k-+B[$qE["-!=q1DrOLoaY-eu+Ha.GtO"2()0%oe-a>Y%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#afGJ5aV8<HG`+2G:i;_-\d,4DXee;:EYnDddHu %=R%3Ao973)>6KllehFrpimjP%XlbKha]c[iFT/BqZ_CE%.n]IjR^R>hb)9SE'jgf!a[0rD3F(N3-AUWq*UCrL$0q(k.;7<AFM=8W %M6^C3+Zn2PQDhX+8+H%HChG+5hi:p;VR>Da^=]#^AlH-S6.deP]p;p`iPu*B$q6L61RXI8Q^_RQ;B3NKVMjRgXR2ociSTNG*@n<Y %_N#8<o17L?h?>MT6$XYoCpteH0:*6Pb^-iXAK2oH0-/lsXAWh_DIH1.So0Z2Z02$/+XVSo</%T,S'=G>j'\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_<f`/")%=r9/]tb_4n%HjUD]]+H9/=>jj0*h+LE2ZIjL_e"&Id %WNW:5't/W9l4rCY:0e&[,TqhDW/puk&mU1Va:EF>O<E*hPs&c]1TL5r6teoNEM?Sq&NTBWanaAdHIFF8BnHBLpdW,LljA4VQK/,D %F4`SQ>fq*h`AsLCX;q!q.^Pp*l"P%VMd'#E*,Q"HV=m+=<C0Z.lFT4fU9UOC!IuSm-5f\o',&s8/tsSJ7@*OWhg!bbKDpWYj+)Bc %$XQL^Lg[`K@]2'l]@*o.%D1#\'+MS#TE>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 %<mWg%:jk!:31$%@,-A;3$u!?KRL]Z92CFFmZjXUh+YO7M,U]R"Eg_Y3P9&d^(+F:FF2jJ/mnBp&Gpd3[ED_<)[?IROVnstf8-Qt. %#`&eU",=jsG!=AJ7k3snE"GA40QqOh.4hYL(X&,oi4UBBj4n%$isqu;'7X3*&`^f"S0geuH#_t!ent![;#E%CI`3ncf@Eg?cBaM7 %L$5$P,nlE]T![fg[Sm$C[M9L;D6(Wm"lWn%d:0aD3`@@W&N!9n$B'W;R#*Nc'%&NNCmQpJR,dT*L\1GZIY)n*1(ND9k,3Yja7j`. %pSIJ.lM_hd^pbp9_GL@MlNZ5uU<18D*!HC_5.k,-ck')eh/"%hki91Y`6$T4H.ag?Ct%1.RH,p_)='s'omD4S/:R)7H57<.B/tJc %Cf#d:L2`(q4<TAtDdWm<35R,-E#q[)TF&49R.+5!!NXRg!=)g<3,faCiGelJQ@aqs8#uha/-@16JVbse`:?`6-7C$F2B2do8:C@j %YcL%FrZDbl;rk`M1Id!S"34tbQW9O#X1[:[fGb-D,@PL9m#?\3Og$bWp-upU8D)gUKI,?g;Y5JIAR>@?e.<sq_lC98@B$?/(;g;/ %e+Sc(D;U0.A3M*n_AP2F\@*"/q-&Z<9MEW5jN=i7YM&"tjt]'^DVhItnCSoADi6t_EHPIHOh`$ejT+54clIN67)MMe8:g@>+:!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->:7CX5A<Yq %6"7'C-13Vj9.XD+HO[b'lXJgN^lk$N&@d6V5XXgjQ.<NEd_Gs1UEj[j8MA?s0P)S@e?m:3V/jdXOqCj:---OQh9ZNZ8.]G9IY/h( %bY_D%O_C6j1fgDT$8a[(pKCTYO^q].A0Nk:al=?-'E#.:Nr"942Z!Tk1'`72d-3=\S0"/`jq:)36'bl!@/9=To,R)Ue"tZQW?-bZ %]p!$:U"Rj?=45lC?4;'F6WQ&S8=9c2]HZQ4#tgo`cR@JSKQObUIC=boWF,fPZ6a-qSdDs.g,0B&%:`<pK^Gn_h@,l"0WQYY9b#1C %d5X$"0<inlORs0#<SY_M,;cI*8F@%W@:1$CgK.4]ag5M4-u/-0TjDu9NGpr[(_p-kB<Y6;_58:WQ(FDn"Cks_\M(hf,GDXp0t@kC %GU<ipN?dDk-W*sO.$Z'o_DoLPl@B<PclofKT8s+HIQ\kZH?%cG"BA^9>r`aOM6h,3%umG5*"P_(O21'u9n!j8,']'*,#U5,7tk#" %E(PI@es4?.s,ou66Te`1<..E!-:ANCI.40Q&<ieSIh:u/dTPYJqd1U9Ae@Tc+N^\]Mj/#V,"L#<O*HWU0cWSF=>C>G4QnNZ,!b:\ %a2g:?,!Gg]$kZ="obL,oW,2sN@P2t;AsNO)2MSe%WB_/lfm8Ln4sH,ALl4A`XFEdd42guMeD_=>DB\?`il*8;#q<P`8F-rJrBV'/ %&F0ZiDBLS%"'PJN/DWOll\BW[+P-n^I6Q0@,BEK[3-m,]4<iquG43L4Ff[TEN2teS).2m']>b_;(ouL1QYQ(%*ggi-D)Ir$FTJS2 %GW'l_7^iB^N?NV''k#!\?+r+DdA>fZ"%PIuk.NpXaWFWKh[*Z?/G/_DYk5sYb=^+Oc7VR<1E-0Ylu&o<O&Yj+a7$1rmF)Kf<aj<( %qoXWjbMnS`gA41iQ\1_j9tafC6pn_TGra$ZCm=Kk+]"YM.liqr*KdA[f@)=@PSH2ijI2ON?.H"!=m(endZ<WkQ>5g27rMW0D2Z/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<u1_&ktrjQ3QOQgmpAh2lR,;,'UX6'D*.HBZ]kaGqg]0'0R(nlH(B!9(QHe<)7JqKfi/" %jbY`T3QZWi=YDXl72;Z'd%$GdBZ;L3`1@KZ0acZOTUHJ"1?RPW)cTPNSM:k[Z$=2lU"3\coiXY+FK1)NTd?8<S3^+qLpl:bf9=0[ %ZACW7:fPnSbnN^jBgiEX]bGS(f:$$1``+aQ%]"lJ.#f"VAd<f.]<J2!esonYYnJ\uFsdSd7r\XdqR!"ZU@b:X*^qEPjlE0WEA""3 %+PdT%,Ip8,OPn>,'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$#<l1`[ %=VT2sojnP\SRq;+j,hEO-'Z^dg!2oZ\0>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;^CO<X0]+_P9jg%3=09a_nc]L@\Q=Q.:9M %Lh0F"ji1NQHgd1]MBfG'+KG`&cYFfl#"dFh\X^t_KJY@AZ:gP+AK4*BS&<lj0Flco<OA;kQ:?I@`nX")W]>00BmOkM.nOZcpF5N4 %!nD+dWOlW'F]Susn$Z!`\oFg:C3QH6;Q7PXcQ3<U]p-e59V+m5TUsHiV;)5%?"-93(qMbKItH(dDoX$=>+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%><?qr18$E_\71"toAFVt6%6A"+W$S`&`!b:t_7bmI %AEsCLc88)=gFSkfd^'>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/sX1<l`-$q2s5I(.J;+1M.fN.6d+**KK%/kU=Sl.:eKDh\qJ %GcWls-2VYL:+50HmeE-]To=oRi&&=30Uot=N\W]?mYl`K22)ku6(%3d=BM(12gA,!4q=W*as-=RSWC_KBWOKDZ8`n@,G8"?AhI\P %b`]IeYC\DQ"un$h=oI1_CfMkJpYMt.8HJ9*@#G`glsfueBlI&uk&P0T&#dF,.`>ST1&f-!0kW@Q&S#`jI[0cY=:1>l<Vhai6Ydlj %,,!,8MC>?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`<W)Og35`NBQgbD2MOHJR%ce(ctuTV*)I>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(0UZ<W*VHu>L!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.:r<o]30llU?;LlBkpQT12<BFV&4Aghk1aWJr1Tj%Tq"7F %hA>G=Z:;nr"+b;OV+91e2&nhb=A0/D1D<T<!Z3M"ZRRpj[W#:>E(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?J<G@I7og2YC<&W3*hKuOAO&VG7fdi?KI.9j0QcWFD;aK\m,kqmZb1h_@6$t"N%1p0!CW];tP$YUB %7GHbV-7@74DE5N:g&&_I-T420CMg_"CI];S_'UEB2qKW-b1ISE=$hP4^hG9/2l=Fl28X2bW4[-7MgX;rFKEXg`MRXXDeoAimD#e% %\$c7EpA[4VrRJ-OVR]0!CD$K/]cn+L<VP)cXQMG,.X/_PjI.DGfJG#0Zp&m&&;!M<.=Eg'I0^*^s6?E,\AcAAatjRI33>Cr!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[<hY\*mp_#6)R-sNMmQBUW/U&iF\b"YiOc:Kl %&4A_;8'D&fYn-1:p48H0htY-2l6i\\!X\sO_R*OUh4Pc,>J\`H>$Zh?`b7'<i#XIf5lmFbn]jD))c5l&'`<ISS>#ATo2"`P@pOW7 %$-9^[nV]L^cqW6(_8P<mbXO23n<TC''lM,2;erJmX:5hf\15\o/@;?M_U9FagfA]p/<o$!o65l03a>OBO'?0n6OQ]KISXGg1Llg9 %UFj!g_TQ0XaR):Sfc1m.dD*(H,Tqf.Ns50`81>M>5<BPBhaFuOLSF^s#+18<GACXdQNGDn+6]>Mq(JK?RnT@P1<P)c-\Oh18X+]< %7JUjIb(""m_(k;PLqc7No`?g9;2f&qX?IX55>IB4[1k\=peA7cNY@cS9t3QN':8Jg\3l8iQhX:b<CkpRj*_r#l&n>K6WPk[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, %<oDlqBW;dRfm*V/MP@&$A/sZ>Q&1,ECnTWBD3^$0hEsQ?GIB>Zm(&?\+b3iBf3K#tY(>mXZZcql'l&]=`2C6$L8WWe^a+T2#=,@% %QQ@@!)p1WVKfF/jKqWFD2k:l26hIoJPU@gK?lV+M<Ok'mhJ]SE1Tne\2MRtYC^us\K#>3BZ^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/^<uiAir=^f_h6pofW&q5_@!AW>[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+<KtT2AmZE$Lo%8[cR"0MbRZ'51Ak>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$(l<Oj''!BPL)ods>N..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<e`Y3qG7aOC;^2GZCWj^,2\up\pAAMr#olYJ1\V %]=>^VWSS42k#.Xk&IHXJdafg;cA7aIKGY`WAN#$U?S=A%Eb![hmmFq!'R2uD0SV?OFGr+(j4:#;PiBX?]T>tTdX+T?4H6JZ<s[Fb %)t,tefJ96FE8nK9eB@3<R_Q*dfLm_t*.sTCMo$W:imgTd"=c2MAM/S>25tEf7`'W@$@`bIF!eeHS7t%"*3Bg6i1Q@5-\qMT%,#U# %oHVC8"Mt`a&48QbR7Z+))GkdR#6Mbsf0DY<H!Ho<)+lj]*d8bS(J65f.$g6"b`#,Q$CG9g+p&6Sl?bsq!>WOB"/]%+E'C[F^n?SX %@%C$TRV9PnSlh$&@&a?.%9REkj\2f-6J>7J<f9RM;gsg:ZU&OdL6ugKV_sVqM_g)DLu:\$X-0cB%qKD,;5WhdlsE+jpY*?DYdXts %.f;B-n8HKc^[MMWku#$L,GAlE=+.b^pg:bPh1"N^N[d)p>ZtPgnb?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:-<WH.!K57h?%cKO!m^HONq%D:6.cJX3W"Gh#4/o6:Jg`m*XGU_]g0OA<.M,uM,,Q(jEfhlg_tT) %/+WJ*`S7^D:X9ZNHR9P)FR[6,Ob0g;2j@JKSN0?1PARQRL8$1:&0qc5ruV%0]\i?%];%%[B0@@Wc1P2HXb#NN"@:"L9C=f#pRUpE %I@5^Z(<Eh]NJg@?g_F0d9sLDo=P&r'<8t.qgag-LFa$Z&HaU;eL'<ZXX1]R+`nG!f\E0uH:NF!fX$2k9dB*>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/^<d3YABkPNaT`LV#=TuU'iCnTnB@:k-:0d>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:(Fm<gaUnti6J%bC`(.epW>9P[fl_G.4_G. %a6<'mVB;7sKR*R10;>U:<l,jA[igDARH0E4eB`,(bBgG)L$RE"kgd34=A:s'@P9P'4>)&<V=6P+8X=[aD7qnm,G3tSk?*hhX^nT4 %>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)6a<CEB%C(q&u %'rlVWUV2i4(AbHijRpC'ESO@g*1*pa>PTEIYnQkeb8qkt%S9HYoc4*\`7Z`J4A'.F"<Xi;fH,$]P?:2mq>&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_<MA0/s/;.FRUS?%iTVRg#Gm4hK3F=AMgl]>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<lg2S"DcI/Yq?!(O%6SH>!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?]<nZRh`s=W-IPBHui'"N1#8;>B&3ql59,>9Lk0b@[F<8\O#X-k%nn-AZ/lsf$R<l %K:R$k]8Sj:3g4usNEkg[p\N"m@Z3=Pb$\J3j2mOseK:,/N[nNR>dB9@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$2NgBJO4<ng/3Wb29"_8X$,pHBMsJf8/eNSqEA'f!1/5S$kX#NA#1m$3`ZY(P<qeP=6g9P %<5=WrU602MV]:b]@S0?O83Bj5lRfOL!OQB8;g'VMJ$DYK`C>F8,RdNi%C*1S&3La].lIe_-Girn%XK$kLL92&]6"g0-T^;MJ`eh\ %\F(ZG\?RD6FZ:IbAXjG*"-OT9)AKXtQ-?b>GU[9\`arG@o-9Li<jmY!*DWBUnfF7i0Z6`?dE6EdObf>o]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)<PMe9HW4@LSYZ]/.b2Z=F:eYTu&<=5ff6:]tF+WnSH-^`g;=#1g\-(R,fGZ3(/mcn'29lW3lQ_TEK %E66!_rm3<U;_am/iiDLmLf?C1_tA$ED@s"64!N-@dN4SY*HVM<Sk,QoSkjC"-Y]bXY"bND8Wk'4RY/lk+?Za59,tfi!"JWjf4=Gq %qoEV%6;6b[Ha])DTbfFSKd$sZa&Pq'S/Q:ebZU1lb_lnPR4^:q,)i-iFQP"4ZAA(A;=4d3kH%5h(^.`pVCYrqs(cp:9lB)lRlSW. %[[#&N(#et2PST0U;D>*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=U<ea"*d&=a-iI_hmP[7u,[6H'(Y+d`dXhP+GL%Xhq@)ptVm-^Oc76Fs7pPCO*0VL3+n*L?=5!Y][=M*c&gM %/A/NP*Qr"g=]R<FU<%in\nhdGm8X@l5N(%aK;8b&'VV)=]P<g2I+uf@%)iiUn<oP_5'uMERjY^qUIcW?9KF9RIUbW1=^?_Ldq5#! %(W<F:mg*neXQoFV_s+F7l'Z/pPQM@g_X,VBE:WJYj:>oSqGV?7S:;3>KY7rn]ruO<LcuJ'r]*9bp<^3!bY\b=T,`eY"c(e;LKk/+ %g'/msK*B`?P\sm#C(4c=$pYPPO4ofU2PR[5jZ_>_9rWO.ge*]A@^cFj4fl1+(0h(l9!N(M9[j:/Xah(0J<d?o'7'aRG(_.u_6VOM %&4`gI?O,#if]6BKC%%A0^T;--$L`(0"E^E[ZPqDV"')*0<RY-hf%\*O"13LFT[jIAg-E8r31C02"=Dc_-$>t5.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%84<L+6!/QKscke@$7nFc6g[j"FHiR&6I"]l!3D1_MZ%_a#*X`:nudM%lV= %DX3Y65NC>07_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^IHsG<c,<@?'UimPj%`2`6*Vea981GL)>c1Mr`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!VOn<VW.?paR?0uhj8^`L[FB3g7i=#Hji+P0'TOZZ0:`V3'b+1(40!M4oKM/p'lH$ %6"f,(f/@^]<f8!W^YZfG<='JnXaUDZdamnZe'ajW\?<AWi7UI.?cXfnU\2`9n+bl60`=o'Jo>Gg]!`F4_`l[9;P0BrL8q%5;4j?K %HL0U93\9dW%uZBO"-Y]R[5,&[^97Ctj838doeA9j:e`tD1b:J9%<cAtW#tRXFh##cMn\eT<A9kA0j`B4_B3r&(%u1:YNk8]WQL>E %;8YO[1pQJP>G,e6YV)SIFkr,RV\r3V#JKOD\5r%^Wp9jiP-;?I/e$06I<J[uGJuOJC"rRF$Z#@)XS:_c@46TQUT;"9am/;KepYel %Pn!.P^Vjp&gWC+?ZENnq$J''NTChcR=W&M04p!<k"YI2?]jDYj>oC";oum>\!:im'bH]%\4ie`;^?TK>aWOj8at"%jO]E.0DdbL4 %/#t+)EQNfk<bo"*m35>-^Y]gsL5C+Q]%,6"\@,V8:Mg3ceDlhf)4ibsr1:p)X^F(EV;=RLLS'-.GbmbX;\eHBX9Mu2*1kq$Z2f^W %C<SG?;;m,#jUe<bK=mED:KBs_\?mj2VImFr6;ueuVLnt7WdrnsIth[QaW$nENcm)3"3ZBI5^rO)9.X7tna_XjY$'<FEp)CtfjH(+ %U`.5_LQ7E*):jBM))@<$GRirF>`u"*91'%V`<]ZZqV"t(+X91Jmg&Y]+#mRaj#%,;jcs']m6]Hi/#a$tgpaSTTV`3c1JlF.[Nd>s %)TSej`ge5U6CQ?V,qWHkYQj:sN`6,2_37M51aI<W'hoK?H\Y+^g]K(AWN^*ISZk62ij1:Z4A$.C(8)2B.V&NenI7ARNeVp#ArdtU %>>AHJdoM8ppXo"R^`Ck]*&;B4lt3J-:.JfR-<[pfUhe/mgKdlL6do9!3Dm4An^q(@duJHA.gYIiH_A,@%<Q5oZ_d:?VE_VZ8S?@C %Cpgi7]"T?Y_LjGPU-cBH!gdO9D2u!&+3Y;km[0TC6f?!3*OZ]o&CCtT<`48(#uO<i1.1s+`QVn;eI?tI_&GL+i(?*iRK;:Y$\C/9 %,MNN[J<-M.66C=rC^o/KFb=VUHK]p?+;`GW4H$^dCAA,NlH%(nkUimXOZ0pa4-5^2qUQhM7%:.X14lVW];kLA#<uJa8WVX5Y&`T( %T\2JK03.&#ZG&+g)0.mEU'BV/TJg28OOE[p=m-r3injRPZh-*s_X\!qZfKRqC<r`(:i0j?`X\%Xb2<S,[.kDi(O?He/ACN;[UK.G %[o@u%_'0&o%F\C<4FMX$qN;-p1EZ97F>*'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*5ZHE5XdejFBjo<bS(`7VbI\ %L?;RM+jHr36^enbb7B!X#$Tk\o9-OpHP]PFR-]'T['btjQ#CLAniO:@o*=pC9Ka!_A8YqW@`T_96IA-p^rq_US=Z-hqusPRLP5;8 %_0DiIl%4!$0XmI`\(I27rl&r#)"BC=[p@srI`2&`qf,l9:b`E;-sX-RPN<kSL#lXOS0(XL<n0mh54Agj4&Nh^LX]53N=&G)&b^gh %XfI[,WGsr7Z\=mJlY?Xl<n_?>2f]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]#n<mO@g<e5(<*F-@2YeV?2!Mab9/9hA&J^a*=#?Jr7!M@VJ5?]">BR53 %K\M$<b+=tDI..D8IVkSD!k,$_#BhpR@C2b!Kbbq.b;Pqn@tgk%T&eFJ][c_#X2^Of*G:<.g/a6T0)W\saD;i$>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:<n"X7*bJ:]cM,eB(f7lcOst/:QV(R2/I(K9Kj2+PfHX^)YN#WCm"/tM]Hq"f7eXO,PD-VMh][8?:maW6:eE# %3uY>J1juuB`XjH`r",5Tm7H)-56<UnPo1'))\U#M)32nnU3+<:^0GKSACICP10%;nHH`4[;n+F/\KUV[?EA]1QKYTg=EF#$9GVHW %RT!CsTN"Qf7P%RL-Z2r3#tu=@K@\:.92^#I-*>_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<s+Yb-e?X]f;HV0)fSB!aU!:cZ\OnLL#4%(8`:* %h1:=/Df?_O$S<`/IY0fNKQZ2$)>:nP?8E&bYg-\S>HXB4SF8bReV,9KODFa:\#`rDr686IbN4]ri,(/$9f`J752oeo#$c%*]Xs`E %^?0gMPC9tun8mK$<+f8?['k9E)[0h<p$B^??&>ncn\l1;m&_7>aukfX-<lXaq0Gb=YF\uT44Uo#0H*i?ph5'QlL$LpjC#TIYdCK" %WAS`CLg+ZW$D<G81_]Uko8e$HlaEa6EDLFX+6^d;TpO>'$6SWun_LPkVrfgWT-j;C@\#'SY9mGtegT*eWNKP$!iXs*M>(XXg-O6/ %BsWZmaeCUrS=V(c0M1G!r%ueEi%[-V*+)K&[]]XNf'NkCW_[+^O68LX%hms=3&d8nVPEbUM'H<<!7&J]"ZNte$LXP^nURbC3-^P+ %<XBqQTb+H0@?Lo$QG?E;nXmPqFmhG4\=_+HO7n.8M,P33H>*DXAn>K_UAZPHWL6\,??@Y;o(Ss,9FLgBpSOKofTok<XL=g^S)#KG %B*nMd^n4Z^;6Nt('/H,rPtY5Jr'?kWBuSn$D[E0sIU?ZHJr"^cB7c)sG,GHuYAoaDj>ok0]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,0<icRYkCo0Ad- %m%5?N&\eh0Z%Z-JkMAAj<25,:-0R9toq#@0L"[E"Gb)VXL]-';pJUZH?2^sUiPls&eT'8X4h/eF%,3%,O^171$HUpkXf"+'L":<& %Cs[K+C0FF2PDdLsB\WtIp!S"ucF79;R<#lCZo3Xk*9P8=E%rk8pdl;\TdXbLQA/MBfC_#6.24)N:KY_R[`HD7m]>YC57AX7V@iRR %#<T@j\N,oBRrCFtbB=AA2ic&EA;okt#Xb!KTQfF&YBO@*SiQdO0Bdd6"Pnbb(Y'aT1Va\'cU0NYrCp.?;paH@+g&>p&D%nrOBsYj %Z$-"VL?9,n[,r"GI'C@[VX52;pG\E.0V-OUp$Yr.Yfc*.pp<pNjKEneRh1EL/0*t%97*$ie4cd>o[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@pVU<Gf:,*4q?=8)2EjV<0<g<n4hptH`WS6Z?h8V8; %9>dVam:bNt2p'u\5gRmU<Cg73K7OMj-aYVCF03-T+'6XaNMS[NA,V",?S@sSqUHV?S&cTrngLYR=oKo9T=+j9(M6EXkhFZFoH&\u %U)CcpPXA:A.jp#C/>!1+h2gqOcXpV[XLa@#e.(<f,AVZ]kgoO_S9S0-DsF/p(sH?V)Y5]d[o%Kij7P7+ZQttce[I]J&'c,I.k:cN %Ykk_/1a1i=3T@IiirfsC2(dEMYPEORpr-eMcl`7_a7`GKIPjVkJ[p!CmM*_]n\%lZmphUICW+%'Rkb2N/:HSEl\[H/ncfuW>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+bO6<Z;$?Y?r+F81V8#29seE<2=d'cTj0u;hb3@;e.'8.n!R)C4E,=1PDSRW9SCO];f,0(t_gW+5&T-CR)qAbXj!D=e, %!kARr$tLU@0[T8_].lY2>DNk"k5kG,q%aG^5N3aNhMArUhnlY3g1ZE)=s@/XJa6(,.RV2cU?e@r(!f9K<kPnCZK'\ZcAT-=ad6>Z %H1-F5e<XIY)=Ek'hn#6p.4<0?ET#!=2t;^1CMN-5D^p?1QL9f1H>?0eWGqZ>.V^'2?B^BmOUY,3grA"IO0`PlEV"kMBBqC>qaiut %rjFAt"Z*<YQ_cUB`:$_W;Xp9*&Uf]+Mnl/b&I@_IFXWpU:u10ac%b,J@<E+PQ`afqVjT8gm\MKg9muBRZJQN1GbHo7lB&f4.q^u_ %1Qh4sc1IF%fJitgkrI&FDH;hIrVbrJ'Di?JJ31`mBd#&O#X8:e%_I+OI'[d>'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<U.]2tCCA+e<kGjN]?=Am+d6m0?7\op5*A>=`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@T2alIa<Q"8Bi8@!1fs)6peoX2'*VQV^H8PZuC>DNbg(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-<GjVK>J^P!TlI.oV3e;IR/N"dMiLK`mo"$[q@5Sf?ohO%X@+0=1G!h?udY7teK4^.aLCV,,>&Q*d.Z %1j6D3UeQQsj3u28(NK%sche<uZNph.o-,M.0euCjq-5YF4'qU?1Kf@=3>9<LUQb)5&+ae4Z1EX?k5-\9*[J^W1g/dTMib4OpY39f %OKp6bq7\J)[KqboG/`7/fRY:C-`2)0Orp_h;A<U5[iC*E%-t>aDFlFGa"=?'H$0+A;V+gdehP1>]:S]4QBc(4Z6cVupZPoKTgV>p %<p3ZOXcTZ[6Io^JKmr&dYX1FTl5U`9JJQgtdEs_86;r])V-'%3@R5gP8@/Z-'q#huj8(P)h6\#lm^bH2cp?.m4+$KA>*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,FEC<d2r=td'0lM]Akj1'`D^Z%;+mLM]Cm3UcW %C[-Q?@Anf'[f$eVXY&bN-CoP]i9?[5A#-WMA%3M*UprOTO2@k53F:ZDN#XOYWCmgb&>Hem).\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,!^;<d\4C]\BS'bfkNGTnh^k`VTI:dEeI\.E)^Y#q.LWc:TO!` %(uD6ap508\]O:Dck@?65m0QR5^)b6Uce5Ks%X"8f\4?32fX/8M45V<G?c;Ugb?lMq*OBkQ1*lBt2V]3.Y+1+IG8K71,7!IIA\K8, %41E^$r[<7dnN=t^(W$(ppRh-`p*fL%Zk+)I3h\5o1aDdKZDI7fT&G=+*[LP8[\I6"V;ANs1Q>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<jX#*"/;?7'RB<sZ?r=LcL,tbd*0n4 %F*-X`i&A^V]u1.>`t/_ZAY)IhZd#4';CFm(2D"\=MPd(j#$G8&$:9QO84!#_LrX3`:Qq]U1`<Q2,POE2#Ob@JUFH/XL1_e]n-^Nk %n-bB,itl/g<cl-pjkaM?To/];AT9KqbpL6(8_DA<GoAnAFUQnoQj>ts#nrAubfGdZ5.WeHCojrPFu!fcJf%WsTccgjfCqb\%lDd" %c"si$9SSs*gWeNnW=Mf'Hs8n-=+$B:jumD1TO)fF[^D+LhC+=i,k['<J!'rijs(`M1Lt37=_He'[EQ*H/W97o-b8r]Ji[9tI;s#K %eCrCbP+m_oOS$du12f,3DU_>;8NaaYmr+;TdJrJAf1f9VJp`_dZWN].hQ$e#@gZJ>Ibu?XN2kbu-XPu\B*rB<XtR(sQ?CO622u</ %gdA9B0$f>9T_<iOcoI?5(]nC6f\4sH>@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"'<e]GWEjEMEP#dtm;>(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:c<rEZlto?ZYsJ7cY2qsRqOYTRa2"lChDBd.[ukbOR/C?"79oF %N#7[nnr"?iG279bpb?Gh0D6Nu"P@H]D"(qVp@Zk1p)gMhR[8:MJ`_,'7I?-)nZAqB$1[?tB<^h&>F`ObL7!55`bB>IkFLKPncO?2 %DMGd[P9TX)&?3p9o`bl94<RYo+1Lq\rn2iqXq1dVB<rY_%Q7q]5WVkK`<n[?2ah;OZ8EtEm7V6.bc5sNFd$j;X_etP'7?2Ge'dFr %c+KM5AN$a-LK0dIM)(>I]?:5S4HTO54W[l\.q0h\,Jn_e>;uDn5:Pt@<Y#-hk5B*54u]iYDW5.b,kE#*\NA$NDaXWNg.s:tq"]t_ %[#J]?Vp.?3\lnb_*Fk[91O,;Dmml^1g4Hn$l.t)R25_X(0p2?%T0;Yo6!$.Vplk<orENCoFf1fbg=5c*&WE/a#7NjldW?85ZS6i7 %>-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^<J+R<ZWG( %g[u1'.Itc@gmgcYF1O/_eEO(Uo9;,0c'HS6#oiDq::ghoV";6;[]MWgA@$"+_%d6Dm&9Sb1H&16aap-XXeB3@Y8SChe0KLl?*sfb %39=KP-iY`_+891KmM=F?F>eE;oqP3eo8Q'R(Pr$'0]dS<d9o:_dc0,R]?$4-F3couFDGl=8d\S4lI55s"$DQlf:T=K*<`3ol0Mh> %=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"<I?/F#U\@-ALU$$r$Y,iGJ\+WR2ANskU9h-dl`u_S40PlD(&5"*f2e*p1n3L9rbuu1G;<ip;POYi %K\SE%L["iE3o3qgN\\a/\qOr-$/L0kODpu+_H-'sNa`@MpnJ^CXg3UKV\?5Pjb58RMhPYB+72U'LI?-i@`tJ)@`EWJ6<2RLf1/mX %e29Ko3o&i%C:CAg/ideVSNZ]>UK;q[YQ!#a.MDY`j]n;G1]r"6bO=&89'u6nVO#,0X@B@tq&X>'aO]?XF:fN23]Qb[Jn;gc)cX'^ %,R'*%2O8s6<I6fq;-YC%l:#]Pn1C+"#O"G_JhLCHR1Z^_;c>2QReYBh1H3,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<ZRc5cm1"D1%Kk7K\$ZDYA:eS"ANb2dJs]h\@^?C %Co(4c3_;#^LfXl==,#e5&Z]hV53)2P=5RhpS4o?S[SCmL>.r(p%Vg%2H$YIVDk<E$F8&\ubT^a6>,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</GXWP1h&;te-IQVc?cYV\ql&%B8Na1i'fi;&!Gt/HrdFH99s6Q_6 %ojlAY`S'E6q]rqG$N4e/-e$OtMpSCJ$T$1$%aXU;$IE]!UX-c/q/J/b5+=4Q`7;sud7,nYkH2.Sgsia&1q@R+k<6bp>.7UUCKH_? %@:R$jOKVX&(`j+Eo>I+Gk;7CTVtXjs*A+Cg)>=#2f_M$T<i6ald5SI2UUehfI@7oIZTLdrSRerb?48+=jR[gAW,XNS_1%a^YD0O. %/2nZ>*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[V<EhnI?D<@ %Mk>E$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,&<G5=g)*(2Ck[Yre(tW3[kGdu.I9^V:Ud)2.le\H;e(T.Q0qCB$V'-^rQuMoAUcMUQ-H!t.PjddJV:Wue@IG'lVUo+SD/&$R %2bN%37))@`-S_H,;-(cb-nAj'kH,A]F3k_`F]9`[QK7q#l.f1K"@f(H8E;.<h95@ld(aB$ql#I=1?`Hd(*,q%ILd:qf(CF,c<%Km %G-ZB)h1Ne;jOoI$HP21a(emBmg*`qKdAKh:Seh9OMWm&JN*klh1:D-?P'P!;\+if7c(aLXT"U=\o+6Q?S<Ts4ots?df>$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*pp<cFk5%T5=bcR)#:$5sq/Nlf[hJrVH2-jg^Qf"SW6p:YsJY5"^30kl>1*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 %R<dVaS7ZQ+`o@1DBE,V$\j\EYVRF_2(&5..STGHlM56'nCLdJ<GXUgn@-59,"S]EKU]4ALeStiDb[]MSL>d3%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@QVmU<U1ootZWMR)qgJE!r9C!4cq.f`3a&]8 %gAL7!g4EJQN]AZ-&)*WrIrLj$X>ue4?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<4<Zpn.bCF=/EW"ZdA#>PUp'%uDVp"koF]dg0^=Rg-c='EGlN3:5K1JXaRLUjc@HQOMj*P!6>=M%= %8UJBMe;aoQ.-Rn;,Q$5X*m[?k`<s@]E6BVu(\oTbScjb>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]<sQ?Q;R?<iA4ur_-;\7 %p`P]7C9-t!`r:'cc/\Ss&X-OObd-=FP7J;<VKLc?ga:].aGrj!bU8#4]^Tm5YYk!KDc'Uo:\,Jf9YG^p^<GGh$3%1$?UVd_4sIL1 %Ul#ah<mA2M,UD:!^IjJ;2S:`brUI#(f\Imh4hZdPXH"9S=S7fP&?EN6HJbAPf;n"Pb@n.#DSFau%Yp62Pe-@iLXK8hTG*'P4mn$! %4]KRC[.h1%,]8>,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\'<ckA@+duL,]eA?sJ%jDZhBGrE`7m4)N^+,M0Jn%>:"R^$I22DSXQJ'$#$R>C?sA,g]g(35aV\fqWP=0g'la8f:l0R_ %OMHuS&e]Gp=Srpb]3DftI;N<HoBLf?&B2;mB;DWj]h-HaP0ceFrY[+rrF_e54UIB<9^A/Di7+Gt%_g*-f2^uBVpGuIC)TDV2>B: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<PGB7J.gos71!,V^jNMCI=sFR$kl%I99uPN*+Q/tB %`uSuGR"CbCSS^!^a\*Vip=6go&N!cTL>>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"W<arFl^iSlXF-\9Pi!_Wi'Bl!PU#bQgp_^)asMce(dBSp-l:MgF'kg?(;2I%rfQ&_8V@r(1:q85sHJ7s/Of8+gJoO5qPCMWCOI %VY[35q*AKCe5BMF8+A[^jsS"$8bH6I^MXESDi4N^HB[5H@F9]aj7eH$DdAE5l1F4oSWFn(GV?NQU%T]%T&elf7ahp_p1YJ0#&>9D %#5`!3An'FL$IdV<L(#Z(L1D@1kKSGi7^K>q[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<A-#iCN@LleWtPAd%ce,4WcihIfJZF!D^q"Ie<fbVp_DJ$6:Ct>+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[g<OpgjYI@rbrBh%KjrS=J^)2l$%X[a7AQZ868u`i(T#k&fE(E60b#U07C?d\#NmVdP'95^A;q*eTsFaQGQ;qDi`. %CS*/6K9^qBfpTa'?31k3"*V85;^?q5$bK2WgV`p!B76l/*(;SP=Zg(R_>cW0r8lFh\XVI[$b\F.*h4I]B<?6dNA6K"]BPl-.TaMp %a^!6]Dd<_mjJMUpT"[*AAso-;UH^?-#NsG"+.E-ucX$FWfDj,<s5IDbpilc$ornCCrGVZ)YPt<:8oD$B?4d6&^tL<JnkV?Sa!Yg$ %odtnFThd,[=.d*fiYS2J/*9/e_Xs"Tb+kf*Nibi(q]C6"<e,ph<1gSXd&\@C6Z()8:i6R^lPkIXhi%>9h5Xh(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@<F5WqS+9SCBYcP\B;qmb-o/^5u-uS)gUa %1]Ig.^qLPelR)C"2nI>=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\<M%eeBY(&+lZGaLJ=7Vr7rhrQL %o=f"@`\ZM#j3K4I@s*H<O4=gd5?>gsDf&/JhU&ZQ&OLsVWP<Usp<U#i`\rhP8>RmC97JUG@4hEQ<l3Lr>;'^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#<!f(A3aJO6;T$c>0oh)"EdJN^@"o %GiB4r)IG:<(#:[/h_<#<8$#A]ne)`.KK[Y?Y2N1JO1=8/NqS.?<Pq_@Lm=#r%tClj\lJ\Br**IeV/b`qcMcscAmdT#\&NiLm2-Eh %gZ%3cYh!ZSRdj=/c*$ji\PQ]1"XQc50B<A;[8@<3L19JPm8nV"^F[:Witp)C^ED7^[Jni7no+R#YQ+7PrN#s4qg\V<hu3HPornAD %J,8KGs#XNd%tFO"roWZskJ.&e07No$s7l><oA_M'e)12MJ,JV^pm6K!XuRL6Am%Iog=S1#YV/6?c1TE_n%/=%IdB;uk9'`Us1S@* %pUpOpr\mP(@a%n.403Q(dO)6o^OOj!`$^$a10.7KEH_=,,PConhu0S\%CZ920=i)RbkTfI?N9j0^<Kl9Z9@t@VE;cOc?Qq-Vb;@T %poaV4osoQJ%H)HJrVulAhu344r:Q-NrGV[9E"CPC:d^%mrr\[>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<JkmW`"a@fGEY@&d68#r.U`s?$1cB3;]#f"pfU#hAZaur_aX?r]V"V#f=khmu6=f %5QSRRf%Nr6i*Y)]+g5;-Q5fpglfND]U*o!Y&-0<nXJ&<pmS3p?6L&_.<^P:1RA:Zmr$3_NHS'W_NWbjHqLuqtlHOTo`\\6Z:*5Mu %dGrGC`N:*`HB.goXVCrsOUNWl%!MYb6X1OUJ<EPFgd<K[qprKB\WBU\P.bL:^/Xco-t#*dZSiLOjf4DKF1/40dNnCW&HWC9NO.5* %.=*Clg+]n[+!S-#(r#Vl4pi-O#/"8JQG(,Mc^2&Un?^tM=:!D1TRciQ'Jepq[31Gq6iZFo9ba?76@&OHi\sG$r5C7<Y:l5XhY3cA %C4F@5rQma@Q=a?_712UMNsH5UE$Q^(9=.D5E3HN[n$N5BOP3@0jQ>;b'bk"gpUf=kkGR5:cJ9s<E1ldPh'4Q/.G0TCSlDImkP'-D %c/@GDhF@\u/(,H4B5G^JVO:Ws!Jh9):ZHouds1^`4O@a-'c.(uV>TnCnYX<Qr=%WhG[<nu/J"=["pW9H+XA!Hmk>)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-?<DS1-O&Q/R7-s7+;J,A(.iq!Gd8IiH0lL)W@J+;^9],1MGjl(X$RYYED>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<h#@]`K+pV,bHHIb&C82Q3SM-]g$#QO&3K\X;%ts&>&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)gC<Y5;@WWL?M_=&b_br%lGW:M1u4`G?<KOgt-e'OD*N*<o'0X3@c\8G?qR] %KpJoh3A7bX,r%YkK<Lt9b[BennaT;\Qo=R0#/gqRa,fp/E*_A^9*K&b<$2SV5XGV'odV^22HA\h.5lOPJb?Qs^WFbYp,as5$jQ(4 %K->r;*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'jgF<o,= %f8edP,i=K]\SuX=a7Rehi75L.:tAK%?^#<d4X[XDZA[>dYnOg^B"X`*[JVe/`qM)cI/(sH5\OJ*IYZ5`XkIp2`55ABGX9A6A271' %aA8Kt5_k>uGQ$o^l_NA77C(s:iRaQ<BO5tM:-=:&9e+gWFL1R`Bg?mQ\cO]p'W#[5Edt2Fbm%dUWl<_,]_(H$?!s<tM_^tlcrX'6 %K=1bPk7)9dCSSlpWji<!A/s7VohL2\!3IBl[AWB,hsd==::b4S;*o=6Oa^[/ELgLlZq$1]m:+2RXBON^Jm\VE"CHipH0K&KSC/@X %>1K:D+``\]B/F%!Nf<*`-]cGo[WCK3DldE&JPcSTEm,7@[pWP7F(Z4V?JN/9M$!Q>]AB<1e>Z+W6UsclTK@*XaHg[Y1@T72XcVBA %G.U7b^b-"C@$e<c$Co*K`Ur)-HKf7$SM\]!$b"4T(cd:j!LQ,aD.4d,MHVkqFcZ.i$02C5/NlYB:PWNj<Y.0e[9XAslO.??[s%?M %\*HbC%m[M<q8[,k7gr[q8\$O2TB86-Fh_l"OA&XlEP#ft9&NCInPBMDp*FQ*6O,t7?<),U*W+O+p7l3L`-g&#F0:ZB0n!'=XNJkH %QX?tg6)L-M?"-PR'+0t`R;k=8_)U=/$%-UBFsu6fDtfs4?!e7YLYe>9p8f]^PAfR"%1-H>9D2Z9\V@XKZ[k1&"<1XZ,AmZn)VQic %3Hh69,L.!(#SP,#ID'X,%r^&X<?9ng6hI(J[&e+R7BAjA8$r`.j5:k;IN6]`2IVg:']%Ulj;)TC\i+X\@a!aPKO3NIlg%hfeKj:. %T+>f045`bG8)a]RZ9'B1Z<+&J>L%MGY/K\oTfY6?JK2.tX(Pc!]8XW\?1!_p7[MDV<ThCkbP'<-XFh`8Wm!_E5u5pl/Hp/Bg]<Wr %&>YYa\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(HMQT<I51q`+mDDTpDCfdg!ST?;VH9WPYgAN>hBuj5)ZnY!FZ627so#HWn^1%FdqF_psqXNu?Hqd@PBq %A-I<UXY73S:c[N9<UOe=VW2\J3biC0:AFhNSp$c@0%h$!$"fIOb;MEo%80mK#`JTaGTGD1JV.Lo\1H]I:,PW(D9tua22/OsC-9MT %]mZ%AY[(bVjT=51;mouAih/trL8V:Tn=1!AJ*h9,nN(RS'5C9A),WKWf`Pu'heCuHX-MZI@J/'t%QIN80dE/gmGb7"HUABm/)Ufj %B^t$Hc_J#o(X8LV&PaHj=Z8WhSd`W5h'B-0*TVtj0`L3VU4f^)!EH@O6`+NqrHN.S$YQ1#qH3NdCn&us@^/Gk'K+3q>cl1@jK9.& %krLa!H#Rf>=M=@76VaYMQimr%<bH2J.3um;EAs60.T7'.C)!0?8+X$$E0tMSmKE5EER&jud']!G_HQi]l)K!BVK_RW4AJG9n:"98 %L!HsEeTpLSnj/-*8od)?&\5%2661+!c_N$,HTi5NZpWmt^%eG-?-ntGNF]G6=.U?G]BZ2+*?_:0#BFpQi-T7Lf&/?[$3lbDB<G91 %'D>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<QL``;4>`_TrQ^9$dmVY%QPuN5("@X=<-"*1_WgO^fsZD#A0<n3s^ZF)-bn6AA[-iY.`.gYcaO]/"DlY!ROe@<a?Lr %BCBK%NW<:Rkkk?,6[@lYOcGM5;j>+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$G2<br50kj^5#K8^Z*$[M;a3IOH5'mI@+m5NLf#>3`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>.?:<YS:mYHhSX/CZI:0%a?H'_("YguT.H[Rt]OZ<e1/pWIj(HjN]>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<FC"e*MVl&#Z:q<b`fX%i-do0BG8`fU]r;AkQ2ng\LrOF %bXN_^*^XQ,dq<h/O=2cXY:e89%8f9VQ$COoVSEm7YlUR.C']JI688HPpppp^YmI-\H\'#q>[]^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]h<ON_Rhan]&B]`l2<FlLM714pZ8-LmP[X*Y9:qUb_#IG<67aYCaTsUb&J2bfL!ki0L;)S& %]om&hd6sk&pf4XQm/gt=jputb")=)4LEpH)_aEc+#)M"i-5Ce[Zg=-I:thUGJ:rq>i^p2k#Z:Dp(01qiUlaU?*ak5\R(N!QblHO& %V<VpP@;WSXec6h)'P;Tb9oRDo=@aE"5QRS-qp>SpNX9oAWQftW&snsa_BfA*$Ju\t']K+K:po@:dKbfgKf<GraeZl/:,(WSWD?!K %bE!!cW;Kq*IBcTGV,&2d#-PGcbS@dk=IBb1h@FMpXMRE6Qs(Ts#m="(=c^-udsuPtKoi[YAE)$97ZERr+-/tsS$O9l&!3D(,"0$c %>0/&O?:-,.KfTlAE&$gE<AN87_:b'dX4-52d\i.B!2RQf2-h';CP8W4YKNeNBIkXUK2YJ>aCD8U@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<<N@!tGpi,ZkR=hMY`F3\e>#Cq-N"rhC@sr7;*AB[>NinV*Jd;pT.A>5-DXu5P:QG#mfmBu?b1-4Wg#M!K %\Z*kk2M(=Nc[j#UX<hcJa*j\6gcC-b>.qW[W\CD''W#mrT.J+3GhO!dImM*'mU<OU'QL.o0YNe7W=#';K_I9mT%[$Tn_$\eD)D]m %Yt#fsg<l*@`C";5Lph9BPfoiW2,KHq);"2CNu?(gh+,Yrh'*UE#7i'i>QAcW'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\;<sPE<"KN:I\OfTghfB>>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`0<r[ %"'dC):0lj]gE:@o>0Eq+p^1ap88m/=n30;^o6i3a"aq(2:f0L-KnL5e6@rdPVG/S!XX"AGWFO`+A:T:he]JA)%\;K?9M,tPI`Sgs %MmD96\0_Y(:]I<p;#/J;1CY10&af[0%F2gEb"uel"$r:mVF_]k5cDB=@uoJ;_t/p;`=Xe^!RFd[0iCk<5[J/4%Kk)BckERlA$]?[ %*pk[/<oSA@Ct9D9]:o)7k!6SM2FkJtohh7>ZlG5C$^@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<Tb+T#K<5#Nn=9CWA$+k,&MF1CI\E820>$'f5?u40TG(7 %Wg8(U65RPHT=1pN3+L8!+BaLdI+@8Q\0&FpQ'a&P)6P#$(q=)u(_q;!$lXqqSK<T!3KliAZt9s%iD>n5k'CR5`sk.0N1P"`F4SKM %[]Upap/PnbD4D`55S[#^"F:/sF8SWsI4lM&-GEURJ]\DMMMX<_.G$hM(]l;h>qVs3^9I%c9Z1&');/o#I]n;<D'I<V.;+)KUlD\' %CB%GS.[h)ci^#U/pEdaO&m+ZR$!1,Nbj;Z=JIu3(N&KY*C/k;o<F\FHdOAf@e>U:Y1^k>_RNPM]efm41A.[bJ]%VF?kT^+d58=sB %'$\*j8,S88S>@C/W3Kff<_s"KJ7Fe68pO`9T<STYe'+L<Cr^g^/><D8[C5j34fmHF]jW/J.H1mLg:96s<*l5'4X4AOb%Z_NdU!WJ %;O4MBEY(`!==U^W*Ju2[YcS$24,Ljg0m3fh-^uXjn>MtZO,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<u,RBPHp1^`Q,'XL\e$edce1-blm!Ntbpt19)(G^)lX_t7lfF.HPef`Ba3mnJg58r+Gi5DfI"$$E:*n</B/*t>> %b-'HX;:Xec7mAphA3W?:g$c2BAII%!iG3qZ[2Yt"D_5Uk@)L"=8nW6:d`sc>SS-l1+c8fXN`,DMP<ISC#6o@Z?->Of_bRq0=;<>d %N2-mm1m--f&Un\D`j.#8Y'9eY7E(:R6m*>P%eC1t&8iF&=r>Q;K-^=(4\)!&SkD&MD*u<DHKtR!#;o[`4<3&(Y-d:hS[Zi)+XmUd %qrf1:dgMuG!52gBB;64o'X!9N5W[A3b]()k\1^8PWZp6Bj23"RlODGpOjNBL$dj\d_a;FF&P@]e5^-j/iW;&?V<1tT<9/On[@[dA %:PcIOi,AhV,-,!Nrg'h4B2T:VZk!Ll3c;W^.CJ?u^bms$eHGe#b4;RaE0A-f$/X_kC2CehjdTcUB6f"65Z?RH%QT>E3)?N;"0gT8 %rI1XF6,pd^gQqFp/J2Y7I6Amhh/!.Rirkipetcg^30;t09/jJKVUd*8N;4dKjPI*0p0g+9^4n7rKkDP5KOml7?R8gRbHU?<X?s;6 %A#2LpM=pu)ot6QF<OJu%R/#oNNZW7`;,'WqT!h;P^L"V>n-p*u)Re8>23=D39Mg_hUimj&]m0Xe)lDN[\e(O'O&V_).-XEp]M4n\ %.:ao"#'l<SndZV^?KBTr[?E<tO2l3S]1KW>!FMQ!ksP`,;e1,:_nG16O8/0Qlu3/#S'EC+ERf/=1mYR_1_0t:?#IW4L60k888&-[ %EVt]gS9Bic>'?R@UY@CZ>jAg]/<i+4n#i4@>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<T9S<?5p6WGS7rJ$;BPh`G,`4m3#l?mjH@3(^. %5-d4211F*2\Z<\JPpR):T;6TkT\9k'mrIDaGj/b_I8ZgBT1i*-%?Zj&K.?1:Xk:O$]e.p_#X:<NPSY">!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<T.aU!PadaC+.S(,aVK2nP%C(`F`$'IcgC+Gk@hmP4_UbdU1e7lXmW)Fdnp3RtF>*BcMB]Q:0f %j1dl//$a:<b3KWLE)]2Q?'-Ci[8.Q&T'K1Mp*)_?=mWL).j`om86742j<m3!>:QU@(jB*On2=a\rcFlhNj+=/0V*4D?V/ZV//WC" %gA2;<\""_oXQ*]Z/#N.E)AQO[fA4!0CNZYsE^k)bicHU8/W5\09TNFHgs;ODM+^$S;(H<n*3rbmB\]+FQk8U6&3c<b`iT<%a\(A2 %gYQB/Q7q6dbS=ac'liP0X:Vq'n_]jg1#U\p(L<7kZprhtViYm0PJrd8)=T,XDi2CN$C]sD-u@ZK.%I6J.T(q'E7j6uRRIGE\b-s] %^Y@P)[7;_`\7HGB4iQ-JEIZfP/M]nEX.C&Qe`q:]$+M0"HE,KZ9ee)c"M7\tUbH!p:fL<gbYZZq&l:"3-S5hI9M#^KY#l_G?<Z6? %OM,.'LBp#q?^tuZ+?Hj.%BJU77;;.3=ZeL)pRBr&k_[lrOK^-[m4.!:;q)YBH?+IV?r0iF^fXCsPl_%GpStij6"<IU;me-oS=aQG %f_>r8G>-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-';R<t;aIsRASZOH>Qk?NDMB9Ad)g?O)L(,Eb2G8=6"`lQpX%(/ja6>[6#]@(@M%C3#<G!( %0JGiLgF4X3E>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 %N<d&i@>7.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<aA2.?85VO0*J5?4W@SKY)`R-&,BkH4kU%t>#aVIC3 %k1cmJOebD`oGfAd,KcJ,]:R,g(-W6f:esBQ%4<C08eeGS>7fAa_r0S_f))sYc.)?C[2iGYOMJmC2)XpApsVTRrPP-3lL:cK+\7'u %5nU.4)AhD2:C2$4(N]"L0!%9g,W#<NBjedNT:nc!At6MK%#6G_R=:mReD_%GG4+OSQWAPRbNU^<!uq4PHM4(p&You#)@&kB5)N#A %C%r*D7>bNL]qNd),''sj%)W9#q)O5t`Ec5_SV#GSod6<.%X0UKn$"reB]5k/Obd4&nMWGLe_a@B.41<km80T"Aj2D+)X!9A')cS/ %BLC(7_Z<hm(*bb\2m!*BCr2O<_i886XG@+fEp4+&ReTHr+_X:&6p*A=LPBN+DM8`#)1#AKV^cSj:g9@&?gdn/Qb$4b-VfIKj5uls %f&9okXeEXj.M_jo=t*B@'mB2saq$l4E(7h=4Z8kV_X\Ja\T3"jO&5)&SBEb0eZL91A8ST`@k8bV+jan9=lHs(p2hQ"G?-ZL6EJ?Y %^_-!oUtY$RKWK3DTA''KfGfbcN%j-'fGk)RRMM#MQObaGKup,gB[Ad'gdoRL\(4tW?XX`!T*#-t`3u<EH0q]9\O[e[H<fQ7NZOfm %//Md[R2brn"<"+6B&XU#OQ\AmTh)t2W#H/50XifijA_2.k(,@p4kp.?'2io!fH_-#+SY6Xmt5R;?/X)i6P3kD=Jb0RJ"6'1^9mIK %Lo_jHhr2N+0HG2C.+nimeH(q+j^ue#N-6^_p[.rcbp8Pb`VFJWOO8AXl+O]&I<=\Y,Bom*4@OdL6H5t/NM"0J_((@CK5Agrr5^j$ %*_O.YBJc&$!ZMKZ6Wn8V?Au7o*(oIi0NDZI/uNOCHU9J-)U[4B62uPLm&A#u?.S.>Tr[uKJui[$jCa$1@HY>\7\<Zt^)/"\k)T&; %jn;Mj8&hT,]NCUS0sNI334s<3m/j"^VYKN13fW41ab+WDY!dB#3>%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<PcNBC,npa?7kn2.@ao3 %EDh$noU)ZO!E,/V2t`1.0ZX;(G;pP.;qk]d7)=G7/CMMe_2RK0$C\9s9ZqTFp:sOo)4SoZAq6Mc9f=L!WdKrmoYrH-U!G5hf<ElT %)Mtia25pBJ7_TnLJ2]gt\6cQ),;iC5Xm\^4ZclXt>;+r.TGE<9dYNqtFfr;b4!bDs=9iU_HHP&WkisA+KP%;U+<!41`3hK\nMBW` %1*,#u%a!D_6]'q,;lH>ZnXkdi/Fl/0;g4WlCe_X.8"So.9t'?\6r9DW:@HDH]sfQ:FcUm0_HDMV>o[A-QR71%CYN9F<JLJ;,U(7X %b$f3`;II2Fj:YH>V#;D*M)<L;^^u'X(\AqA+%7'_N1CiqZNW40B%/>_i`=P\@jYh6TgX@K-I\4\<uI$(4!lQ>#E+(i2q#86$kmG+ %[Q.=L`I[s!.-kEGEX^')ru9;a$ulR))\0n^f>$$JT(gZPQSfVl)c't4m].lEMmi?.#Ng_K<L\+C*Wm986Z2Lr?]O?(`F;1!ZZ(Yp %$j/o`*,V-CSO>"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;UV4<KriL %Z/J7WgU2,cHP%#.CW54N<<3&FXoUWESH/NsIaJNl]?O>K7_:M%Z)6+,K&<k[hO>=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=.*<Ws0a\3::u8W>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%Zpkkj<X3r!LZYAHSjRf@`*pu?"%Mk$j$J\b3"]7\t*?+3O^Gm<!ec2 %-e+`A9BM1jO,K%FVb>F?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+_P<pSLP^5SXKR:E0goXe^@P71tLr_41/oA %R@c#l23<#)!!3H;*eP;IZa'uEJ`#[C$/g`S+p+?kB*PVL7l;kt;BG[ne0e*oYjcKF%==*Uetgm=SBX8YB9OM6@:<;DTN7^I)h/M% %QRT^`+eU"8`A1#0B%FOd.++![W+$[fB2kSM<._mG6K36/"+V(<U$@.]r`\\a#UGKPlm/+@,%12*Z3V;MLis/MMT#Spk^hnGP9Hh@ %[&!Q[8i]9/pBe"K6Fo'p9/,/f=CS1n*<hoD*";YgTHY+;jr'_q"JPf]?]hA4E.]f0;=e&8Q0:$Y8G&lIkG2oZP4W-\4N*IQ]!'Z3 %)ojO_A/=mI5pPIpmI#d@C^93@n0c8ql%gIeaeLR9q4c9[SP!7cNW*B50o:=1JLcX7MTCc$)s!I.Ni\7("MqmY_RuO?2IcMbj`/04 %h4V+!U1flkih7"Q]4r&IY!<4h"$)d%FVkM/8j*T@8\*+*C)#:/8-:9gc>6r6:-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$#`T<CSdS9ZMB'["6+'J#/ %dB/cm"?U<?7oc)'7>Z`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#O<cL0^dir1)c&^/S %Jn4`dpIk^@)+IZV6fA=ipJ)%Mdq3mD^OU;8N$iUa9,;`E)_.<<gcuG[L;SDE!L^?#EeBKq*RJ8%T`cWq#=QJfLdDui9e`k,%q3Zl %AHT3oVK.2L3T2sd;JfpY5Hp;h@&'q0k:R41KE!ub4:D/@`E>Z@k9kk3f<m[W=iU<XPthb=FG?QVm*q`W0dh#gIQSoB8g)9M-H30^ %+tCm&eI8%hO',/l'ZBSkgP%F5gu+Ruq^1bb34DX%mIsm@]f<bnG+cnnQ4,d:0aYlQIqJ(;7Ffk/g%8;MK"Do+EUH5+/5`PtL#O"9 %`hbDjYlHs?_UDaOaF$`pM,!]W,n$>kLuCNX3?PNGi'1Vccc"IG:AI/:&p&1A[+-I"[&.7<!:A*AqprId]=2X:gFa`Il%5*P*WF8t %NRuRP^Sn_[^aH2&-<_g^3u.!#]\#Z(%E[9_Vb*j*bB/mnQ6nj"42K-hY'21mA/>7`qRC'oC+,`g"o.^e=r+jVTSTg-J6\,5r.WeN %*PGjW!`pc2mK2P6YUO&-eo58U8><O:LYA^3F/M5V5I23t4qWhW^bDaM7P,WXd/hh@J8_p!T.KDhY7auD7aNjb#)ZMO=.@9p3@r"- %%Nc$m$*mUZ0$\AL<A1BY>bh'-l3tN6<]<UhI?>!,a>/V=%TFQdZSc]$Lnnl,C-)?FZCZimM!6.5l1J\4A<c&M/*'[4?sjg`1)AD5 %p(_8fb]0&.[//5t6^#R9a[5fU),L,QDKX\tOlR"Y"?'"I]gJ5X[>u=S6`H[Dg45/+FCAcY*2qpgq0<\M1/Q^ZM><bO_?eSnh`/`d %>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<u$5 %[OOJA>'WI-9c0'+p5<GDmSa8PkE>!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%! %-T<i5-a*7Hfm]NU_'TdaBlT:U*RY>settD8-Ggp!X[nk_2BX>J.##<hV&H]:+K=c51H<P.-<EWn+u_%qWk&8=;f8PY:oJ9\>`IE= %=b<BI4V3d3PEqV:%$bb1Us(i-LS$[+R;I(Fd:COre0\PtFK"AtF?!D1X+HEBT8'S3YV]np1@6i5@=q^Y6BcNFk`L!'7+C]JU9ZVg %^GqWHUi'4sCUmST2uDT:&F\o=Fsn90Y)9L&<)ckY)P!h+9>&pE_2X=Eb@Zd&,l%3WIq0e[`#+3TU-7=R%u(fY*ao/+:o\+2"T+q@ %j*h%^1[6#M"uAZX<Lq9#$f.Td6lfdmd(X9a@Cq4'_K%8sFegH?E'UB4MEnFM.*Opng=g_k-W6.M^?r-l<^cq7,chi\fQFFO@Z\[% %X1:02pd6PZfQqbUSLK-khsMs@f\JL<'I!4C".*-5hE`FnZ,9UoI0l$PTMC'\6UJeg'(irYH9XiIBmh9L)d-a_#GZ;poOq3ZLj5Ml %/b0=#V"#'!W$i-rkA%/U%>Qe1G^%,u7A^9+nku$`;c#9pMU%[&r3k!-<4!ie[X\i/5DWH=]!DZhAIJj^<X`9q:7Oc;N=>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<bWs6)C.OFg<R/X8;Wg,"%3Y^?O;.bM[4"+Gl:?,e\\&2&VSc\+E8P1;A$*G_22k+/]TZQPj+Q*n41-FO\[,ti %5Da$2:>`JAOh\t0atPtjk1<Q)7aa%c@7Y3ol4Onl4!uC-a&.[%[9`OG<TLaZIpaTkKi#N6B*UYop+t8-A08f@-`Y#mfN"H'aA.Sq %P<;*5*&K_C53/"ZF)+mRO=XF[.YY^]]qa<:Sls!j]4^pSEg9;L:bkZ/RX2%]QlRW=(!M2-B3()\.Z"t6O@mru=F<:]G5KSkHtG9I %`Ep6;9!%QYWr[5P.Q6e78HilKLYqG"k_F#r"r%)RLJ)qZls[qG?-Q#7m$eKl^b_;7^+'4,"kmqJC!S=cO'Z:Y4r]`j9X`@;LQ3B+ %5$Wagp28qi,LMMc!m,/Cp'.22+jOH6$m.$Y^=._MZ(<-6b`9b"g,RMBs2';dR!Oor!C2hKUfSB`Ct!2O;/nBFMYfV=<T2`>&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_=;A<LX+<5IcT+ %)cHfR&]5K_QFYCEl>i+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!TdDWR<a\L!^06fZ[8UNF$g(hj=<8\F),IQ_`fT>M\5W,dZ:I=# %ls%PlL?/X21_m<r"*!rbKKbpnBEE(Pq:)_9;<$dA]Q[u='pk$7%r3GnC`J3KWdVM7?@F)t`oql/*M(XM\<!gQl!i$b$S+<;MD`hY %,;Ed?#OfVd._/(\7`)WqWRRO_aM%TarVsf"$+1ClI&Y6g2O617!@ne4-09Pa*\LENBK?B7S!O:H."XgDe[AcGoRd!o?0cW1Q$3G@ %)G<+*MTH^q&\E8];bD@ej46Nt#6`ZpENk2GLmDGbOSe#k$PS=2f"8X--6C)=;TuD%ZHF@*LBYs'HtKS+-DZ>^QikX0OJ64!'&\G* %'h\SM%!hsO,363RFBJ>m,,(5VbaA9!Bir;L[%EqD=k)':RY16YRQ0qmF3$$COl$8CYhid9'c,oNo.$SkLO)KboC%i6a@@0<I*jpm %N<Q<qhg3A(5'R!3b9_n*ml?V^D%I(R(9/pQ3QVNC6[%F615:psc7%*WE8;aFfmXUegCQQ-.E6RDH;6'fW5o096$i_h]=?9d3o.0: %@1g;F41.*Z$JS"]F>f[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;U<O__XXJ;k0<;4tjdo;'W^H!M0OgXf1p'ImlC_AYG %ObbhbNe:&Q'D<[k,N)tdWDYW,C75JunNs"K$fo[u=B%Eg[Sj7l"!'V2S['kGmh,?*R')WA4W3S,#WtA35d<\!$;U2Aa#1+SWb`@C %&Bu/Sfe[AZo-h(l6ASK]VsM[?M_24U'IT2O.m6Fq)+<GJ_E*4YK_QEP%YNKo!g2$@Gd*ATdl&&E@#Mo_ok.!(;D/a=\%k;>b2>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<u6R&Q<i"55C3SZ2B+[lrW[aYL!D$S-0A44JUr+E,Y@C'0$jV&0=5T+ %oFZc^A+qG264pXf8;'umYm5A:,B2i*Yif4a+G?A&CDf=J:.Em1EtE:63t@rcd9mY!,o;i1ChB>(1Xk?#(d+/h2ODlY+;_$6%V7?F %XAB)qq\[V_b(K`sX8,;I6b<g6+An73#U?MU^(\_.U4.t:/l7V$,aj'1dWd\ln7nc@+g)RjHC/sn8JcE8m4@nRid<_Vq9kXKMgT9o %VMWR>\lBW<<9Ihf=u=a$;tcNHMaEN0"Vk6#B9YTajdo3VLDMq5aA<<^Vu38W(WJ'R)J<`/7%c%U)@lVrXFA_7Ad((pZ,">2ZiI<h %AhtV3E9(OP;^VR+T-SEk:cD!/l)YI*$2k/E;m;\%$oE5b&AEt.I`<N+RZO#`541.!*P3ESqs$e,PaOL\QK'St7Kjr=I(JA\'STOk %BTLVT#%,"Pd(sn8fG9-Z]0i_<XL%3?L@B]p;8gMHniO,%g<Z5)jQM;%"klq@2%0^poS]BG@[St7nI$\8A!^e7ETX5as"JNXBM';_ %H_n][JFBA$a0cNB7NV;'Nt].p,-qX:Z<&WTPe:!@I#(bMDXEiC4\[=]A+Ym\(jR$e<$:&PQR5ch-!-DG!F\hsid1n>: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!0<ag:27pL()_th:d/F= %XR_F.M.IDX2I4@["^ANga0cL,6Y"nMqV94!IhH36;Heh>Q?.]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"<bmZAIRW`onWloP%r98P6boOpuq`E5b"dA3qEK(%rh2^+X;)LBg>o79_.d7#Wr2'FZ8B`Pe4i\Nn*>"DJu"569Ma_E*9, %!o$[nLfPUA@D]/_.2W^Z<Br!u[&:sPnYX_T5H-.0Z#?@'7k?2\*Kk2&g<jWTFH!Z6VKegA=2eAkLd&(Bj'<@#8SOlQ)\FEYcnL8@ %62,K`d0r=C=Q_[5Cp%&s492[\4(kKa/'^mJqa[44C#f`1rJOC%*E!825)`H^?8a&JoFpS0Vh.(BXsh\u@&`PA?rLp&?Q:PQ\F1WG %7Q;`#X0srkQR(s12"bs14?E<tC4uaTT@pL>[N^L]mYH.Cn#nLf6SCPrPIHA-KttL?E@M%PB0iNQYW*UZJsWd5=,*3B^OSCpinXFr %LiR_OKD`PC<M>o2hI13J?;.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`%8j<U="qZ8r5bm4`]`JRluMe'<BYujPA>c32=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[4<l1 %mLR*5`DoE.!G^>Y?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#*<d;,q.!GX(qPf\f7,Z3BGe`b+7$"_P[2!f73\GVRDKp\F#1FLe8R""\?3XMeMMr %*QPfM1U%C\rQJBD>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%[,.<CjG=47i"e^54f4-[1QSV'KMk?7o&,W[V9Kf/6&B]?+JtaiTt?4 %N\2gpibtQ):F\m&HCq"O;CR_+kbfFHRO]Zq\`A&[V6A^gbNM#Q\+]Jp2att8eXRpCP?%.4eeT!')ERbX.?`5>?qNpUD-_)DaOpnu %1O]u[/3Gc9TZ!:2<R<I3iRcFc/h*&*0uKF]n20\%E2Za;8t':E!&in/8!cBV=e1l1.b,-PAL1,Ha>KPMP`]+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 %<ke_t*n$.@>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\<k<P;9oFb$Z!#;G?%d'C0Rcd %%qd^7!TtEILYL3A,L8+fApJF`_Z3%46RbXPf%]ajaCtLN0h7.l\<*GaLCDt#lNGm,Vfib!k;a7NA1H6m)o1N<M[Ob%?2`"FL,oRp %K?h(/1Q8Spc,U$K!s(ri\..@uFDbrND1S@\%)>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<cmkuHaB(%oS %/\n9,*ss\W9Pt)>)$:@^J))ETORE?__s\YF@]aln`H2<#]7G8Uo^u&R6uG7BkAjb2`fU>nla57WgDCPGk_:R=$i?1Uq\@R.RB7FP %G,L(P<Nfl1%W186e-'&qVEodT(`Y#l3ng[:'\A/9b,UC[?*AILESQ2H_:7bOTJ`Zk8Tdjdi-FM^,B09_5iGLLi3_-+&](1=2l%Aj %m\.d\+N&t1<kK<Sm7-B3E7lFi($H-#\;?,hV4$@u.or4dl5Y_s6*t8B.-tYR8[EfUj;J/(V8STYDIqekeF@7iFbA52;PWlXLPpLE %NKFZJ3Okh%1bCSFl!FCgogBQ$fNp9uMGoVA.FJ#/f4Nt5Js&:7!A0F?:9uP#7>a6og,\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<Pgr^7]Sq2"i3OPoFe4#"E*]I?BLhAB4(6Ea6K %CDr(W<8S&\c'l[p*pdo4?lEUI3>"T"7caVJ@32/]>WC#!fitu5Aht)NPp)8G$ikt`9:Zg\O\k9R,6P7nCB7MU=]45(inO.AdXdZW %&:em"9iPIn1AjYE5!#Pqg.4PHR`<W?;nH;!0C+acIB+b6)'au$MWF3Je(+P3;9]je/a&i'qYkuuhJC6@/@uDhK)CE$@R(XC_Wi)T %ak(jC&:t=U75J?X7cB4_BJ9^[,#e!gqi#-i^!F5d2DYD$!L<EGf^3Ed1ZfN*]*+irTbkd\@iYI8cE.^V94m10l&Ap)IKj*5P]qiC %@[TY)RX0L7(WNL#30coK`fs?/%;tM:M]E@18&a:Z<mAN3eHh*VCq#d_`/ldD.)i.)AHJ@Lj:k,Ida=]:p`M/Bq\iu.2g#hj@.\Y: %3Q*)>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(<j7B(H-L_/3O'U%7bSV[cefp8"dC2p!0=b;T#pMetqh4Orcthc1 %lebf-QI0^8fT99DPW`ChbUVF[#Q_K3TY5p-2>,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=$/<o %!)N,#4UkU#5B?@im1gH*kI,1a.7QJRi\qNf[L(9bTpd&_\QD)+G3(89NQ1EO28A'ca:pGldY58&i";Fe"0dM'J7"!liWV]3Z9g:> %`DuS8n2sbUc(I,Xa?;F9Q8363^$-*Ok><PCMG("LMG!Q@f=mL8fM82Z<TC`-8m<*9]1FnoU"FD,f$s#cS.(1/H*SaUM<3OR;GHcb %Q=+c2+>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\p<MD$kF8Ofu-iQ %7*2?"@ULLV$rf,/p2;MS_aKPk/=p.drpFbehG$OKXRnc2CQ2G]'tjM\j.C)XbOQ)nGk;@7gdPRB/!3I]b;`b3d3f#HZIhUM?k\)l %"\o4lFXloKg9r]BZ05bO^K-$NQP$,_28YYSi?_b9;TuhVc*%WSel[O!_\q119$^m"AMCX9d/HbQQ]VZ1fnWrRk$h2tSBH6Vc=VQe %q9!2Q(,j;)#2Mu,ho0<_XNd3#'_04[<=DX?h(:E;OTYcHoaa=n5938Yb,MM=P,ohQ&cdD`'`-o4-bi$=0L[D"jNtU6e!G)aR#Y1X %irR^6\=GWYKkXY9>e05Z;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\<b.tu=UEH\E_<L/A.9.V!jTkjC;n/<S^H]SjUj49 %FCuM/;iG1.V\>hY<!p=t,2)YKU9+-LnJ?r#3G+_.p*eO8nW-j0"-2YVQ:7]Ej!gPDRsHZ<Dbl-aTgrK?]]itC[%WXL@[,DnKp,mb %U2O#%^c_1[]`ae>la<(nQ@b&!"l$J\C$7_h0S![_)WIuKp.(lQTQ#p]S2[=Q'<it-[MikoQ.Bp(bY(Y:g*E8><D+Elrkb+JO38;_ %`,-D%KZ,$slSnR0"p&$(.V&l;6<NC?)aeO*dsd="\J"+"^uVhhHRU)T]W59a?IB5rbQ>"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;Y<PAUZk+ZN@<18nGH`_^L,i79Z3%;h:qUZ45L:T;Wl-f2"ND>5KJ' %!D,dAZ78!DC;psj)a/nX6rk-CQr))hU70=Mi#$!9pg:9lnVSfY_(97OCcK@9PG!<ER<Re=W4E#M[l]W36Z5"R7kG%M#r3uTc.Q!r %=lENT/)[+oJg9N@$p8L+TtJDkY:n.El\ePLV9$-U-\HEH7pjW#fja;d=c.r6!-XT%:kd<3UFWVtl*-6M[PLV:I&20uW.c`'qu[c> %=^PZ;M,\L+o/p0IL5?stgUs8;@jc%$L\nHdap?Y;ptNJ,Oof(dN^ud$3Y.*78XNgel'O*KX!0hLp4Gfk?-n<M8d$+"d(M9lXWN"1 %&uf$VF=S?N:2&3YO`Ad%r8uW9TQY+pa)?0RZ;,BLp.+(f,rrB7#ar(^YY@RLK"r4sVRA46.'4q'.!>CGK`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$<F-.WKQq@f"6(D-h9'lIYAWW@dO/B_YaU5;D_#_^^Wi#7]NNldr4tKUKi&6dj2JgXlfub+m%)dEq'c08ku7m %N-QZDU>00(bV:+ZUXL\gdtX"%V#Z!S/gR4_bUdi(Lr#b+]cZT2-$]hGQB[=`V,u#`R7N(]XIs+Kn@gSeTFX:pf.>QM%<q,khj[5h %hC&YgQhX5,"[u2MZG+R!#`Jd#hu]4o2#T4VTG+Bl;f2nG,)<T6L9hap@tE$lRj_2CjhRR4"5(!!&EL6q:V#hd"_Ipl(+#8cJ68M3 %V]P&2$+kD4*MareU#nKd$QK\S=#`+28hlP,;L3,a&@n3hIu3gmV<,FWb5cr.1PrF;g^Q]OPNk"rC;<uV0/1CC'@X^k+5k&,F.*9Z %Kg5a1LU'*n#kTH$*triVoF%MeW%q-)8rRfC+'$7H]O308$k-3P#>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([<URm<gREj,SShmJI&]O) %V5:qk'7bPNAE8ZXG-B"R2A_oq<oJ/9bZW(eph+[edDJ-MFH>pROT)=AbWc_"GT)Z(E%.qn9^+aZY$DHBW^7TorjXWLYGJ+N;(Q/] %oFG1l$_-Ve#\,AE:5uk4ml8EpB7],ihYE_=^%YYmIE(r^#5fg;H%HB'<V/:91c#G.*\mrH8P",$X`,r\r2%IuHjr-Q(YN+fP4kEO %]s,4J\(i8o;b!p>]$JM_IrEIiDT,stW:4PcorMRsE#i(X<a8i`R6lP!V&.apqeQm,XeAUo$WJB7=,gG3*-@Oh_&%Yh2+:-cRTdb\ %6MTAP$_5[mQ^*rNFB9V3!YVJ!+pIUOj$F<b:<dgh6`bLq+nG1S;YSV\?]*"$GMl_">5Ar!ZOY9f%.k/=acbUh.7"fcU:Y_8J+"<A %VDt[.9NbEuJJs$>W'reC1ItOEfO5EiQg@lkS`fgO'edG-)>IK:>m6]/\>H"Q4O<J"B![pC!t8&&^P%#=a^<`S>PZ]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_o<Df/7W-EItc8?IL"LL>0L1#4RigU5X6K3'&'X'7&O8S&u.4>n_!pUqi%bXtM9d56KA+&aHmC+4lG%&BtL %898>pH89^5Xnf@QG7>E(H+!"-ATE+ge4pJN.Be8:SE]rnf1<U"B6<CG9P`<_=Vk"jJt<7,1EknaW?jhO/Ou&1`g/2F9m;]1:47YX %%@_B<eJ:,50X5OMU"UMG&GuZ"r@B?TQ=O^aYl+>.[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&-JO6O7IMQUq4t<s9p*^N(8TY(Ki:+T4i<#W %MVj%h!S8,VZGB8O#gm41<ei,]8-9qu\LU_V"_`28gg\TR=#+sJ-(p1T%0$*3_:5A`]g^(-;)^m3KIRu`"2()a1Rf0SE3$&78ST6F %$IZOI.*a7$0\;)!-]5qkM0JdE(bfo1i@2,NNYn:X+uUPX.\II]6W\r'HEDL6U"qsG^O/!6U?VN@:](.7(J8qF-%XSbc7LBn]=s>Q %<HDlh)iE!K;K.kLXErl#Z),AFLim*>G!iP6]st-Z'ZHlQF^,P)_;UCnUR%]P6HNg'Iujl2RA:m5+#$]pem>g#?'Y2:0&A"<I8*2Y %T>'dF=J&,U0F2%-!d<(ITp^\g$FU*TE,I8<C\-P1+YR8n)Ai?VS-#l3$,teDqi(/(!_RD^&Qko-P/Oj6Tr+f@obiua+Lt,8n6&Ts %3&`Mu0X*5gKAI&RX0I;"ld!F]TZN@MICP$FIcdc[4sQP2CDrq>r>K-=CVbfq$o:50OG'TnQJ($E#6,Mphr9<pAD=Z:BR'[`8):L$ %XC72sJ_pl)cR:ZRkqj9Y6Esd(c[_Z^PKd^R2?o@KO`I."9Oaq!##KCcW^*,SbSGR%``n@Lqn:^N74':4FS1SIY1:d(jlU$,V-:dm %c*\S!";E:[!HH##JI^+#f^]U_2K'Z-2FpS]Bj&kjW_%dNrcka=OY7,&T.4k+fST*7W:@]7grWT@lSupToVc@AEiEA0nnP@(a^;p, %ii9fJiHK\sPu1pJ6e0%f"keu)1/nbKa\A*n*2fGB#5LBj(d6K4!:SJQA8-"\Hl=T&RP"pY#R[lOc!X#*7:hmZL]h.Sj-h,$)RgOm %iV3\c(fint4CM(!I,h?F4sOuKj<i>'X<h&/@9=A^LaB[PL_PEM2A!r9gDKrU.?O>6GmZp=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\q95fGUroOskoK90<A$ %"=0Xio:0G>U;?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^S<A[m4"k1oZpS_m<?AVC3s?cH43R\^BV;<Mr,JL73fsTYHD- %RQcdljd9Y<hK^oL0F-b#2Z[@F@7A6XM;]#Tl]0uFB7RCVb?^7hqtc^^Rsdb0dPsUK0!7JuVY,YsKh,'&%TR5TbrFFX(Y<_g/,4S* %%:ZD`:!C\cl:E+04^tSe7;sG0!585D`^hN!@Q#CJJP]^_?6/`l&_`OL.Wt8%BJ_]M/L5[M^@TohT+I-dh@$f3_P,ILG@bKfEMLXj %*f>RRU4fkoA_?19U7KDc7\OJamSVi;BqrT:VTk)EX`4/a3T!Wk?C68!;o089Y+\US<\J/j<pPt?R\=D$Hj2I&QZQQ>=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[E<EHEJArVl&mGCc9Nitd0b"lhtbqr*2\SE$2VTk,:2k9k=$K_m2?oJano1L(pka=Fj?s9ZhCkD]rTj;+N9.- %Ik;!_P3cD0g)L,d3)N8M).R\(+n-nb?";h`Lo<rtO!-bq_`]E^"6A%;SWP1pHM5O+hE9"X7)u<[<(QSi9VP"<8^Xfsp![k=Uj]fP %PnTsnNdIHg<0X0fZLaA11%nu7K&T$3k+'KoFbVks5V).Cf-.rd4sUK+iWiLQk"7Kb@9ub!6`VD@^]N4:=sj(pcU9p`ZN`>s/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]<PuLRFZScH!E'e,m %-Vm)C5UrET_?S-lEs=.(%ISbDLa/H]o9dR^X)!X8nWCDAQPmKD;5[\&.m\XpR=N;%.C7Bd"*0]%S)b4N<HW!fbR?I7YK_CZUecX` %3H&q=]WB?@,f27_8P-H[0&-`4UTb%2PUR4;*6"KdQkik<'H(<0BdFK,fQV7a76qhN+!Zkd6HHQ3h"aO'KV/g.LDr;),Vk!ZDDlLA %/&6dZ?k`iR6Bm;"Fs(bW&UB*L#`04s/j]<JH9E7bUU/+X>/:L%"nG;'$nqg?2DF_T>[Jh/[QV<t)@@<pS6[dbHg@Zg)sXMjdL(>l %h:*.U7(et8V@2@]PtnAjb]%GE5AZT\<pKjPL@>P]Ch[3)G$Y;aE>Kc:a,O^e/4JW?:C>s15nd00Jo:[q6@H'@E!3"S(mdXTdPPJ: %=06r%L"#R&k06<<ZInA@=V#]f3C$,r`*#oNS<oGd<O?:MEHMVJ"r2;(/DBk<VskHYm'T]/kbq:1<i\O+o[WPn/dd*"=abZ/'uuW) %QN6(cP.]aiEAnISh-HM%QijcJb&T.q?H+0+<1=,*40<^5ajXJUhI+>9G7/K!%uDGJB0jC&,@32%;OG^!A&5juUhRMXr<m;;+&*0d %A3kY'QICM3F$FNZ9G?A46m'uNFO&noZS'h.d@I3/TZp+HQ>,L2G\u&:07>%99CDL>a-Z+FH\oo+bt9loks]CufPG!0@4=HO/ebcp %;fbj2]^gCaYfW')@U<eaM9I#EBMnU8=Q#ZX]U=19N$msD"P?P1ZIUHPi!9OOP?VChLe9K=q8d4#<\_luPYgt'7@)UrX.-p)?Zcie %/)]j^2"WY$!2M-8>O7HFfRs'W='u[CS@oiWeng5X2X@K/IW#3(]?QSmB@'':`MQZsUaiAZMu_ue.'7_<ULnZhF9Z#IWt$l;g:/1r %F^QmGNiaX;j>>$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<tfjK]`e\uZY)0'WMBZI6!;qd(l(qJ4;24He4s"L?13,>,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_(<SA6I!.p^,5R^ %$'$F$f,Qs@fT27g6"@`&K<[1CCQ58-M-el<GamRH?![$%cD`4lJCh@833G0aW0,7p<u$k_j/TCMm"&s,Y[+*X.FC/Z9mc3(h]J*' %`e^#k885@(Sq)@9XBAZ\RP`H(R:ZQtV^aa_98\S$Q=Po<R4d[R!a.&_eP%(t@shtX0He6WIB[)`\W^E"JYBd/X^Z6!=,g+'Q8L'7 %b+',k`6gJpQ?\B2c+8cF"Nf2-/b]k!7Bmbi,o`i4!\\LkYdH1O\O6m:p_@(G+;+j#<";&_g@r!PD\9>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+<BL**O@q'CP^GAldZNa#,lBa:n7#[^V:nrFlML(cFXP/#(s`@.MO`,jVnKd0J\5`7--B %k>c'Se5eB-&lS>j_jY?h0Xnh*NdWLFl!h1LbT1L2`RNBS%n/C(_-RX60$q;]G+&PWa;pb)+1^U^U1EXtb8$'rA/e\iN5D%^^ths) %KnQLf#+gEJ<TFQB\JUO<(>03[.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)q<D2[JmNAYQBQb)GefpD[3kpM8U:..O %OnU$cT%3?^.DhZ!Cj[rAs5'ha9<2HM@0*Bm#N'Ak%jBtd4O3LZ>1>?@>;#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*%@<c[fRuP24]UBH20#@BJRPFB %LAbp,LIUm../`+`7W]3b][/rI>:Jbg;+X>!F-<HcS\a,<'W'd68[525hosRb-mE4$5!C+CNLMO/Kd*?75-uh!KRp7\Q9b(`&Q9.: %@MsnD"\6E"QC#"Nq'gW1;H1:3G]0X*h)gK1%>49D[Aq(0B/"?a<AfgN<fot<7:Cg'X]J@4pSCl.Z+<^[Tg#;p-t*]Z+!kcoY8KqF %ALCQ66r?+QVpD#$k;\,C6ZRC`?jaN($^O+M/%/K0=I4TWM(Iah,#8WXp8kp(BaoSO&k.dZK!Papo2"&=cV'ijGhYq(`HGsa_%8O0 %MnbO$!G\C@a]&<X%2Dc\nDN7'Z*d9.N4oB-9NOM.gmEr7`XAZ.$<C&9\C!iTD..E+fVl:b\8A8$(UX]<e_PPG=?/8Aef8n;ht+7' %$-8t\q!3e`\TO:c#-]p_3tl$bW6CI>7d1i"<BGYo`"e0n/hlW'3WTPM$#%8!2G'sFefd'QgnGsmWoR>!2J;=q8PZ](_[:-!7MCo/ %!=,&L,=Hs;*VJZD^6Y\tK`Ej(6i&$D*@$Mt8\^U><To9)Y;I8<K]WX83CH?=CsG-GPS2qC:9RJu$)0FUQ?%jd+!cN@%]2)bINo]? %$3)1oZ!58OX]e5IV$UKjV)cnn*e+'Z0-gcr7h5"lK"Z%>impUt*,6o.5H8Em]!<?O.UMIDUVn<\/HZbVD#.PIKHIccf&e(V>ReT& %.aNsOj_&Hc4='j*msQ+oTh70Nnb9^cJN6/<YeY(XN)*gP[@mHKrW?7'TG5fe;V(rA7p\uX$W]R%_0uLo'f8q:LZO?+n8P?BamgkF %",WZ*g@7F8B'*:G^2oS`CF$?M\YTIrctWuC\8gHlp?'A']d?!<0^YaYXh&(=?bY]=Z#K(uKU^I&Vc&[iAAQ0\MiKLSbJernbiS8G %*/D_9LU'IpCdhls/p*PQM/0L.UJHrZ+WSUgMpnW4KKg%FI+ecn[:l!QIQCq-aH=SX2+o\O`m+Ht^(>fQ+@!)cP$q>kq8eF2:'&.# %;].0!)q)j'?,Sq[FV(;TXTd8upajkuL.Q@#/8-TYOK1FE@pDD("D0rQ4P;=qcad?J$`[WkigmsB=Fn(G?M*eK?OQcU<e1tE5"%0! %bU]]biT:JDih@t59f2,CTL-*/J7d8(nU_0*lV`KW]PCZ'qFc_[[e$FbOcCJrg,_fI2uohl&a:u)\nRH5qU@>)Q3(]V!/UeY^tlj^ %I/]Wh"^EYFN/F#;VG0t!"8:_Kg&g!OUKN9&$<e3&@EUW\"q;0efOWU0Ao4NM/"Dl?0s9`oen5fo[7CE8@gFG/P%a!$(7Itp>7HdM %'j]=CVa*^1dF$.HHmuik<PC:`'i/RQH10CO&RZF_bmlE8FD2A/I;V"&5URW#FP%:8>%$`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^'D6R<ZV(rJ:Q@>oaORc)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,YN<cm5r7kR,&>SQip8$`%0fu&CHW] %FN3,K!Tujc+YIO]K/MB46#bn!&r@k7O@=s=U_ZP"c=?)fnnUmZ68qU(53<5`9HN4.)FlI<o!S6m(JGX`SD+a5;f6,h0>ZWO8dN8W %: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("43<gG=G%[')?K1K.(.27uJ)J[YQWg$mjVC#[fgcD!0RN%")T.E-k853\FWZ5C<6Dp3qe!GG*B[sJ/ %;a2UC[$\0CGh]PZ)qde_\;=A\XZ8_ag%*J1a.U4sCQ5A)cjWuuND$9E9`@:s1[1ou#d@6`+XEWdQX4O7`97qR!>W$+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*:.$<DCI][[?2$l(K@T1sdht6?X59=Q"XP@DkJ`0P;&3AD)Z0;:;,-F0*%J98p-$obg6<MUsD@h/!g(>Xb^[#]aNg %%]K@sNn`Ws6(+_8:\d>aWX2X^3-H.:>os<:?-KmVO>_1tWktgV?km2"WFT)!MUY<cVLn@f2Z^9%6r!n(<$L9YA67:uBF<foPKqFr %NIjk`]8d_GOWm!KV.QtB6A@$\IK#J77B3AN%bY'Eg4^eqg!q!6fG9P\!Iq`C4sGQ`*C.h\%"rFMjaQ9MTUj.C0?1YNM="?]24Gln %($*=@NCf7SmQ:rB"[WUkH<>KKZ+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+<X&KrDC)*^_<L.fqNB:?BjS;0W'VDB4"2ZC!5oqKq3XNGG^$N-QSN&'#97TtdpqV_fP3a`Z %.<B/:ZR/_LVMQ@6nm?YHHo\L]7f6>kV<ItSga5bHLt[M\&u%Rs-nX`??$Gi:-!q!T'ang$X16tS`Di%2=G0mF7aAH*n.9hX<'/>F %WAE+^&R'qQZtXQHZHVtQm2X,]!8lH@FGR9pfI=ZeEN7ciMfD)N,4X+%,H/=,U.T_Yckq$,QEPt2MRQoPE]&^W8%/2L/VAl5<Nql" %H*4)skUH'EU6D)SVDL)R"VO<k^N(Qr/]tq\YR35o=^[;"%]6%C9=\5[bGZCujg0]<Q;hjeVgQL^:\e2XW&PO+<&LOr@0J-FE=hP] %0SKR/#cK499p*o_DGU>,"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]_'t6<Ib9Ft>s\!(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<J\0o789K99C?.c8HZ:'a>$R'W1F*kmDs9c=YeGo_#P)JZ80\e3gYdl.!4."=BQD&<S<TVeaKBNk\Ci#e!39G9p-m, %9MH;?<hc4f@h;s%I7H/\K^k-(^3UmEcAGSZf1umTB!R5<VA(Q/>*$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>hE<fdCgVD"c.17cd<J#:[&Xl"Er/ur&0*=ole^WTe*'m`\MHnAaG4$$IMOF[J %"G1FJJ8S(4Qh*6anJkRI_Ojt1J,4uuAL,EpdZA0TW7Zb?#%E(7[PYms:MikNgN/9c1DK"]J-c&Tq##rKK(fQR&0">ue$>Lj#@U,r %,&!.s:`O"1:%D54S;nKh-7j65]PCXn>SSdb;LeeaKPcECP42lfS\d,[cu$98@U#RuDR#2-f:bYV:i!7mBHP]^3oDX<b6D<llcR"Z %U$D9P5UUKdSgcX+V?G)HWc"fh(R)??%sdH-Ui>d=#O4b9k"J\j=6Eb;k-7L3PQ`V/\HNMB:..J9)-K_=#E`-IdFP?ZdUr=gW0*sW %C=%=N7Z3JGAoLQ/=*%uh-3_U*M['mok\aLg*9<IoG[`DP":ikc`HiiDOtP10EZ*f@<)cf>^<%;9Hb9&`.&D``\B;\UTOJJ8WkJhZ %!*nJ\<<Nl,+B0E"\md'+<S,mUZ(EqLHW3E8_hhsN>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)-\<C[T";jI!6S0U?Llk"R^<#VerKnG!0MX?2k7EB%Hco<e*IpXBYIf;fR!icQW4Bl1 %!BlHBI)s/:ZI?nYb#Ndl\tk8]C8NN6cu,J2a&"N#(_oF(HW&!r-=Kd<*FL)rp(pIXcqdb^.BV?=IP]P4PEJ\Lm7@.0VSNR5r&+Cj %V/ZatAQ[+>a3A`m:43F]WXj:Ho!Ql>0i9\2ju'q'n<K:T<5$5GGY>A=M2!Doh(27gF(aHrZo$9oliH>?&9\pamSEN4!AD<rWsZ0h %MHhN>NNe!:<G*d\_U5tA3b#kC1=fa3J5Z+nWD)H(_5%-JO[P;kBJd^GiaddE*s5)hhnFPg1^j9#/a?4$ZL9tZ7jS^t3JT2KG;K_N %J&WR8=)qh!2R"C4_D;PKG*905N$1:pkXQjd5)4qPBlY0ej!Z@8E^]>-0pH`MJpA[HFmKd(&0*;<3ZB9d!$Tbs&-Tb`q$,J2Wg<-H %99YccKi?kaS5k,%"dM-sDS,<I!lkiq#9!heQdt2p`B!F$"tq-HJ\RiIj^BoVmaQl##.+cBb)r^9>uj2'AgGMa]h\O=<"p]Gm1Tr( %A8-29N<d9)]Pld@RCoDm>Z4ONj*AgoP=4HB7TE2fnBDStIorF5InY@RVkFZ3A2bt)1k..Z*F!JLN)a!?'I5?ke!=hnaIgF<lsde. %m:Ok!9+4,<JUj2$*O49_%#'QDVAYo9@HtsCh%[#Z<V*B8.OO[Z>_APhEuLS*H5?1_oYM",T<Ku]lRZV5WbTose0/)"LV3s<HV)26 %b'WLBTr(bf+8_&^okG"r0o'M#K$XbBY!&`OQ+Rb_0d=SAI;S0E*+e]bY5Z%O`\@dL*.HStdTfpRja>eoKf-%O\$9.]8r'a0G>Y)S %R101h$B',?XM`XD15Oim&HkP^d!KHd6V&FVF./r]cAT!<VQk%q5_S*ZkZ:kiBfGRdk-QC4a31n^YIOP;)CJNB&K6nb_II^c>Mr:c %?=lf?,Pme-o9_Nn,cNk`R[2qZQr=$/i;'%AOHBcD-oDlMR/iHs@nS-jiiJ.olm\PL)]OP#"/c["(4,O*K_Vp0[=@JS<b-;nPoU:` %E0N??nPo2!ie5HT[QkpIic$fnZ:?H\_5qJeh5,3tPKjoS@QHi[MMI/bNWm\=8lI]pQnU:S76#m:=OrS,8#V,.P%aK)a>>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<FlFgQ9I)A[E'k/Zf8@W=B:TAJ?"%-&lYI^k#e;j>[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;q7BDM<b<pG- %DN]ZfUoXp>H;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^*RG<D&@Qe=*uG*bKP0Sqd<#HcT6oXfQ:!`@+1M)j:<ZHTS"^:Lga;i8lo(rSno#h2pYG6LOVDd*.ZO9V_Zht+=R(D- %=i=sXq?m=&GZ=_DFC:(_0X7"-2SR;:q;GC!0dX[oC!'1]q0@$HL"k[M9%])Y$5RDT6oSn=?ur0%2b%jt!l7@BPad0[Pu69R:!9Ip %@crPdTmQ81jFJK/r7"40e<JS)guV@$-QcOn(-_X[@[phF"+`D/b0Y1;#<]--*pq>J/_7jJ38JL/g#0QB,3t($gCqsQA#l1&2CAa] %8h*?)d=II\F\>rcWhRb-'dLn4J^0,rZW,@)7CIg=Ch>8c(r#e_9<)unO<G/iJa!4@MR#$6D03=+fmZ2M%DdY0V3;KQ#&J)0=!3gl %d:1k/lXF*R(YrI!CH@4GKQH_8IGm99"J"($178AESIAQ&XZ<6N@$sIAWk);47mu)dXllaPr%-iLX$Mh%[LO,]30r?3_mdY8c[<he %No<>*57hfgbTa6\3W=2cFe%->8lAW+\Ksmg$i#Y-bJCn*Xb(pq=2+Xm9j?Yb>.n'\ESh]/NSd+TT]R6gW9n>&(94Pb;GhrI9T<Vh %I+36mKQ-IR5bsR_ZN1;5jbHt4q%Oqi(mS7Uf:Wb9X9r7O7+aR4%I'<q_]euJC@rpaeNKsi!QaPRadp5\-1fh'NT*aoo>I5iH5@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/<h>-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!<d@&1caI1J_H)hJS!^b6JUB7B40<>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[/p15oLu<s!^Bna8KL.b$"oaR7T!.$auCSRR;pAl?Sb;c/&u %6=@GJ"I\Ka3*9ml`hRo#o58#f33"[O<NrA,6AbiL)t6AKFs/Mr'@]@<78?]%8J4K#0fESQQU;?<XUpJ5Wb$sDgf/`>UqLP9?2_`O %;DCh[_Zr\=cKPCeU$p'EaKSJ8,"\cZl-O&o9?CN7F/C33E"HH3Z`n+";cnQV8<R$$cgPkN]#aE5hX<+WVno+0.c8=AF^_%SaJOF( %g+2rY@pFei_$J^/Y-/AI2=)]J('&o<1UpLgc.`R%m7m?Q(@lo9%(D;DJi/i@_i?LjA1[ZWJ=b1A]2^lPVp!4CbGjC/bLtGH4T"ml %=sM@#o<faNVB4Wka7la8%s+XSHT,R*m3bT"SY+4V^4ud,];k7+FVp1H]?eh-bLp*_HL^1s*?rb7I+u;oEAc?.`*h"nB?=gaUqc(9 %cQK8*%#Q>h"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.DgSIZF1RS<t$b$qOPc07S/+f(BI48F)&o%9J@lcNTXj/-V4*>I%2B-%2[D5h!=]'dQu %rt)YE=$8uC2_L4%`s#ZQL6o*"O8BG)TATXe?[Vb:s-:@uoe6@$^\XSfs8K5%r]gA-J,738r0r32Hi<pcroX7YgV<KFs8LcfppYbL %]>+<&?h>F>Omt:5'dp5l548&=g1pH(o^/j^+9)0sO+7.9O+[K*>Pn,4^\r*N0n&pp4T<t4T)VAugD"kjK)L)Wqk9fJRfE9Zr:dtt %OkZ*"o]9;hH;8%\=bZgn9F.YegKI[UMVC_8')^/\F'pifP:D]#$1L&c'M8-E3+qM8nT6&&%er!6X<F0];TDX$D[K>UeaK0ET4\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<PUhNF%K7s;=(Fnkra=#E&6XGC5rXuOO+WS;kAf%mWJqnppV?OYW1WB8i99E855D_IMrV@2i %CIaqSatiEH-3/ZKSfjQX2,0A1W*3?&>-h!iM]UdgaHJG1;%eT$lDh3.=5:9;%\e7(%gKie:B`7A-0Cir-o[_-5l\0jJgcT7<1iJK %Bgh*hdVVLOK3I]k]O]s)gH$0<<kj+^ER&qm@Kr3KUnDG%iP\h@>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^b9<cHmKeBi %0"#Y!Aqa)Ra<LS[1kZXC&ig"qGM:@dNEWq$CB4s=&6pIM/`Zk%XWeJh9,*SO6c0p)SP.#u!%\d/h-1:8=mua7OVhNl^1<Y9To4=! %&.?eeJm;Zt5MF*24sh51T^VF['J<RX!<@%Yeh:&&,7@5gUigZK!G8UDr355f`ioaW=E0#//#a6T\')o2AE=bP"d"5Q(duVh]N5p$ %aS2<6p"1YT,hb9<X5o.,+r<[l\e0f4JE#2fO;+0"08<a_]BF.#\+pO\X"LM,6_oo[d;5R'mDIWSIQF\,rTVibA#]Sm#DDEUrMR`) %je,12"f&)EQcRj]p9<@0]#a1=LA+H$N:='><H4-R8h$EO2SEX%-j'9"6k?i;VH>m/r1PEIML:/f(ga!nI>hSMIA\lk3#6^pN.p\A %6Pe2Xeh,<uno6n$89<?-eM\Y7ZM4JmHW&SW_9);uk2FE=15.Aq`cKpKS"O*kN+3KLLpE6>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,ODk<k#r*L;3mAnZ]B9nO_HE?UBAN%c2E<JNP%!.iRVP#;bZG0QWEJtf"0(k[^>q0\,[3Y.nb01_<\De_T<h6i*RP"hYn<.IaI %hsdg;[Z;Kgch+QA'0gTmRhd?nb9S<+VMm5onRXmj0c8B6R=)9&as[P2j>!'`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:<d)f4V07VU!' %+bM/5]],V6iIq)2Q3=7/-2u[\F'FVnep93GIm,MmL_jW_KPU?FYDR8hog"%3[7g']4V'S$4U`@RF;#qG,<bh?Y*5@b\:"Q^FN/s: %PIAtgDC9k:'/[VuI44/Zmlt1QQbnXYH80h(li6m^&LsaePk._4-0I;sVW?P,Gt<p4#,f$&R:Uqq109A6LXTjOP&GLu!$#o4gYV9a %OGZAo`LEA#b2eE\K1B1`QN^+HTd:g[]9WhfiZkoV!cCd+/B%JRJNteqhWLqN:kT*UmkuRs%Je7>C`\tk<+iX&%M8\^>=cb7;r)%E %\?'+>Vb=c2jdDd6Zn=$C<B1Fg>Ce@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^O<Cb@fY^g0RY;sRb7_$_E/O+b(LF;WNFS(Y7G%E?piN*.9_;Go<33,;8?q[&dMp59A\S2A1qeBg^TI%E %&:/eM-b-oWlk`HqH!>PTc^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[fH1KO<!4QJ4^gQ2)^?e %D3Q4<OuVTpL5PZ+S\6G#s0uoIk8\>PnKu"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;aT3B<fNjH1#k,`'1^'*F^j(%@#NM<[+iZZo %a@Kg*0o"_"mRa#9;GeK4[,SSeN:.%QOFCYfHW_M0n_JOiqDi>NV;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<<n>;"4eampMVg8=9one!1p+3(J&hZ<rDk= %F.<!X3!RYRPc@5:#,nF<JKdT1!Mr,0`N)._KV8-5**-4$"'Cda:h\6Rk,FGB:S^NC?l+R1\!-8b!*Z'+a]jW"5I)440ar\G`;+gW %'*7ca1)FseYitQe(-s(d1@,q#WCC=c&&2dX2I9O,$Rer$)B&$68dp`@:mYjIikVpCGs(DB8:Nf3LUt4uJH#cLX[Zi)/=`t,es6hE %]lPj+VOJB#Jk'8`I7BR1AMo"GWpp)_R4aD7(-AMTO2-3rBRAh`g(o)JpQKS9MDS:"R&aIEBS^3hmXl_mfB87FZE-b0)3?:EO=G"; %QS/e9)A)e]W>B4!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`<E7YX]I*aNfc_EeX+Us!>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,<VtBu%0flOYZ\aHXY%D7A9iH^e %7,^R3VMH(EmR/:V1AWHUi#Y8T6=k!TJco1%^f&djWe$9lmQl/&cC3'r%t`=3hP/:/%0^,c*lm7Ze":^e814m$aXp')&3:b6*j8pl %O9NY1;P7YNShuTGi+h,'DPR=+6l+Ij@`n-aq34;;'I+m3\nYDC'0pTf<u!Erj`+B*8jHZ(k"E,;T::FDS*2rPaI9sbeV1pi6Ro+J %j]:Q1@.=5gP-'h&*"Aa5<42Po`R3S!J&.&1-QS,01SJ/[]SB$o@pOV^#Xj9,Ot>XV0Uo!a)/OAK/FXSmdTFYkDpoE=5p,JJW1#O_ %aL0I+a`.L(I_W#-9UM8M2PU<Z(WS6[V(_:"<H,A1!_#ee;q2kD]5G1?/Zd*VmsBK\#rHsCLSPY(`EQb.N!'>/[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<UL[#`EOSlk]<M<d3<Xo.R]8[L%@19Lb,JVgYX=s86liSN`57*\pO#2@5hMhk\>[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-R<H6cmC"Fd;#b]>25@e@4@3=Yf5oOS5"*CGTO6I+S\'bjb?l+:+2n9VJa-\Hrf!H*c+kZI_gmNWt/0g88k_a:3+Gn;qUQc %<ZK+K,\.E3!i7@Y#FqX*C?,]@4(YL?BLXN/_GdCqN;,9g8;UM(`5EiT"d1qSjqRA-dST*#Qu[G*D*CAP[5sj.3(hi8O;t!K#mU3S %AsEX)RRi$]B[6m9Y3TA'!Jn_6Nen/gaOPqt(5k=(n3LP/Gpp7Y,mg=/YN0((8Y0k(&>P>h9dn[?5VIT[.W9XuB2D!.jm+U((1kZh %8(UbJJ/lbX_Kc<g[+K=o796>k6ZE5?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<X9deAUQPPCC3>_bU^Oj1j()n';/<<X/Rm^QO %Ri7=q5#rXk"1$IC!LFm7M3(=:8H!bC]j@!N?tc6SngS&m5]pp:0%sZsaIP`j"S!-@g-'&J<#B;Fm#/+QCD+LN>Ic^mM/M`2id$%Q %cJsa50i%o?fj*<n@mr@f6G*neSlT<nF19EGfeba>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\<DAl0BT[$>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 %<Pp)uQ)X)(Cc:#Vc4s\"g.e;)^Mkkf7*iua#DXNTduTO.V/JTUmIk.l^<I;CX&3fY>23gt=F#HaJE#,A<53O;&AI$3Of5u74:!YY %+!'sLDa3.HHF9iuLhhOI]ME;h/jm<P/Nb`pokIm?6$T?c]QtZhG('m7q6S-$g:%U\GJ.8rD+g't`J/ufFaD?7D-gl)!?B#ghm-YH %DoZHN?V\2&eNQ#.=2'*%\+<e\U6lSnj+F<7Z)CuQLA3beY2MVgdJE?/\]34dh;Z6rM+J]`UO#VPZ,MlG"$`&drSULWq=_Y\52GK] %Ek=ujHbUS*l+2.A.FABUil\_@me4&^9!5g$qYsQ"O6A]eJPYa>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$q<o9e5:X9bZDOW %FK[??:=mb&T(d[SWU%,W-5(hY+!'\!Rm,`qCEjb+*JcJ([<THH+D?GbO%Da6[L\Dob."#2osIeM)XV(;I<9Na])*?/%ubtR%/P%c %inCjPme>tBPOqT2=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<fDf6YHn<hHp0oZ=6.$3rZh!9!hq,KShi>[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'<EU(M?\s/Wg8%qac+<QX)$g>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>Q<o`b.=c(3g;s@'IC-e]A,s)ZaSBA4Pj"<f68?*1=\Z6h5;ZK>Afnl<dLNZ/&6PQW:BRB4_T5aulrN'Og36IX %%KWHd,r2.BQqm.1OGjcW'1t4k<Bl==biJI0h\\%*\MLie^i^+V=l,1\ejQ@Q'flJ.FA;6U%j*9T&P-k7%GTMMO;<BKfkI1X$mnuA %P0HX-`k8`V&1Io\:MQi-(f*^lqk&]MTZPhAOZFK;_b;^fW3-0J%GB2?:V\'[JSaWo&s),s?1)K#pgm*$G`4,+q95`]J-uDSFL<kX %'cW!kTh5V\,/L<(Q=n%0_89>8!Vp"LS(PTIP5u<jK(<)JMN1"<!hm\/mTq0_E(AP-Uq/`lm-:Z7'8;@mYN-b&T^tK0k&Ea8b-DZV %H\#Ei1>9gqCnsLV-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<BoO9 %ReP::+8>(i,o1,&c_PJ\GpF2GdX=k*>&Y4C"HD\/Y@ThCp;T-gWUe<Q:()@0l8P<$EkY"8)MN;TZH1)]aK8uqgRos:#2K;FS]pRW %D9f&UC-:'G'4m:![G+m&2Tjf3Of1I'`+0]uMkcQ9^B-4U\u3:ZVkej;Se&gcV7(?W3!kmr^,V<'CgFN?R41l:MbJaD+VB;32Ir2: %V#lM`X?T5X!^/W7MWKtfb?ho58jaADRWTN_`I_l2QT(3YUW0sbQm"6m4:Irtf\_4h6fV)e)T<Ke4)ZuQ-:1rG7+NNgZip+('bdnK %QuddXJ?>eC:mp'pD:$<52l'8U8aFiFO5<lZ"p2uqo]fj):<7(i/EZBQ6.qM?!@Ep92OKBWB2e@]Eh97eH64^I'uQW=JdnoE.Q<F) %7`cI)EBmtRe'.U"T!)iuBk#-j<t"JoCRej[U+/"\CYnaZ\Mn(NRJ97/'`eNgEc8\VL5=BcOnD(B1FLq_cV9EK#aS'ml>akl+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^<XFi-o$9WhTK'Yl_nsn9DWo>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(N<HP?&o1Dl %cL`>9_Q2dX;3)#p\M]6iEYF-_*e$rII&lq7m_e[iln)Tk_sI2<MBQ"'$_4pPp_eWNVJcX4E4Q9YIP-uj(EmUl]>oXf?rp>9R1k_< %'a)Yo?gQd7BCl:9\9AL$^e<?ahFO176b@``:u,&@X?OOAbC]9D"O-^9%TW[bL&D-p1C;D660qcCbtl&V-tT;+2;^UR^;0@g$Vh@9 %#s]'GIL2oEQi=6`'q.LrfpPqV]kB6g=R/<iqH7XSCbEr4G%kUNJ<EY?*GW%3/]S,k%,"d""#,.N4=mSO>73heWKIRb1stl-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:.[Cp<RR,94.KECP^92-.TSmkGN*$uHo4-) %RF.b_-#+!qJM`7uMnZ]?fAE:Dc<V+6a:jHq78dr,(L*%4!c:I8ngq!u;g:K^[M-j@#%\G$f)!Le1?aEo<=Kju.a;$.*Q,;'pjNVh %_<VJkO:%9s>0RdHK>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@8m<KStP^]:O.F[aokFuTt5)7_9bgp_>W>4:tW$6$N1h,$tE,U&uCT"6ia_iZN<[6l9a!Y %L1,Wa+KKbo'?8aV<IJM^4hJi2g=Te<N'S[.[e/F6=`-Rd'[pW<(<jC38mnJ2,Aofd_"s@-4>e+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_:<oqu'!3?HOE^*.XDC>%*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<l %>$WIgfp@X5Hlus[VtJckFqf]RLO4eB._*<nJRL35V4`>rS<EK@*WR#Qb]NBVas7t%Lr'eaK<coMm"OY,d]Oh6MWp0'J@dDZY*Fpb %oVqHDUg4%K3m&$_2ng.HSBPamT8q5%L&!;lKc(:dE\\aKE57Ip"q?<k)_AY)E2k[t-I<\jF`b23>TMo&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<qG0\n3pYWa]Q8DOa) %q1Z\R,L%p%JRo`..Td@<ja4(3Z,/9iP_Vs=?_`sWXc5#>;7Z@01`L"mVMmTWk9+;f?jd0<Q#moK(*]6s;@D)gO;7(i3HD(:,c8V, %&2FTO%jH),XDc;+CFS;Ncu#m"GQrJ(focnS0kq>[A!'k<I\Frr+rBB<S?CXp'IkQ:)K';_GhM4Qjq]ke'Ss(hOd_aWcm\;3YDOqZ %/N6*98SX#/ggd_X-"[8/cP8kU:G(rUFkPg:,\K]s\9EX9<e`UF_mBc0+gZ3'(Zt'[[PNt(;T,Dn\OP/oe5bZj?tS8Yabt>#r/=ci %R86Ubf#9#UE/<S,f86"+7OY?-+:P.(JD^=cqB/gQ_A#DhjPB,B=L+5kX2(KhUd08AUh,'PRfb4?g&<B=BM3WRCI<u2-oh76[[V@j %*j<?34@J2eI_Ej#,ZhkMIKV2h"<=;IDb'U*Qm1<c:scVN<6WHE7Z;oCa\X)BAij_@9%noEpsQGk=_Gl?4?bd]JOOec\gWAPhHka= %%[upDa!9_K,-[#GM1kKaneF]dE(9=2Y9If<D$RMSG)[hc.9cHf"+\cDQD`a2U]JOf2E\O&L?c=l/gn!EqpWC&*ABG4\9-3>ast8l %E]CCs<%RWW0='3@EcnY'A6+KJH$1$LO;g"=Fc2q@R?9bGp&tb<NjCAmU^eUp2?prN$3RFa3s4LRErJo#]uG=\VWkX5&duO[KO\_l %)b1A,oOq`ZnVmAsHhuue;L-I;%=p`q=*JOQAXB]O^)qu*WG0mI6kQ`oXk--B&#lu-V3ecH-Lt]cNESdA$rBT(Ka^rQ!5=qGQ"G.H %GUeR3qan2e5O?.,g@AVoi&\g)CUA;//k>fX;"uk,F*Kn<#D`B-R,>j\Cc^2_L<"j'onge#OeH?h^I+j:m*uP]B*)-:P<uME[pkVX %ETW91__<:/i!q5(BUS[O5a;qX1R-^]S2/nsk@+Ko`Z[ojH3;>Gm*[*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&#8_=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<Mru/=@kT>%;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!_<P'ehe1!$pLSp_F"85DCW@;o$,;SFN:a]EY[V:?eSPl!BM?T+MsiD(c*csq2"X`VX%`3hE9sYd0Ok. %5J!/74;Ru'S84VO/aGQI*hUjRm=*a/VV]Y>GV*<tEF+:0R&P(P*LjdO?/L3X51m_U]:RcWK`Fg7k468HG0U1Xc,\*c^adG&p$:@I %pN_3(3-'E(VYgCY(57!ol[A*0mX!dLoBP_q<kAUCZ.ig!9s.uDGJn2j)`P0':JZD>rlMWbfBM&=-AY1%cV@i+\DR8;n?ob&!M&l4 %H/6MY@qt)fnbG5#J,A>@XY#\[("MbP#t&9iM5]9%s0#=l5O%hgnE9c<kp0]ciu":W50cmGkqLE0T=6/*f3Gd.Xn-F9"nrrU(u<'& %mM:6E\6W>;DR\b_\&i:<].1C;h7Cbd7<@l8+u)O/D)r(9_g.^],E8%`?dFi%\*9Pqj#W5,VUV3/9@0s3[r0Y<m`4q7AN]99"[*mc %4j%V&[%F5:n9O\MbY&fJWc-@;rH`sSBQl/VgnmG^)rLPdVkMi-)$AJig8E9^D*#USXg_`QIiA1T_t3HbQl>dGI(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<iJ'on.^AqrQ9Wk'>>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@;#<dD<aj21&J-[@Y"Rp!2G??F2!GEj4N@_-`;j:0H]6=!!S64\) %[eA=u>Vc_;[S>uO9VG<S3@P_HBe+<$VVk/&\)0EW;)!PBa&(_#P."s8jBo=2P)QG'0bF%%NY0q_a1A+%ZSXPEa_cRoWsJ&u>N<bl %>tm3/;;\IDor_3Z1+>YX[74g(A'q^uX`C%AOkI].`;^C$7t`S[\r*u/@9(>9C,L^,_5A[OcA<MgbSr\2YIpH)^tOHe=E/kR"sGMd %)-Cdb1'f/sqsIGj5u!_1E./A]@*.;fa:=pFE(FCeT\>gc4SB6bVV=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=<jl`9Q_-2gk!$k7<hiQo*B+ZD@]mF*r6U*^jW1)p;fE%r< %@qBZt!Ng@;*%WYP'i=?W("qZhQsCq(@%Im!"umqa1+8MLV*mf$Z/&At;IgIbGg,>-ND?Rs/g,)Z=hn/(;Cd%Xc>.6<g$i8]+2ds_ %7tZRkQJ+#_O2f2&KZpZJN>'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-pV<jCQf?bZ^^6IM*)$E,N)7>4W_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<Fhf3GI;2GtH6-!V^[!5SY(n.u*9PgNH1UF$i7mWcTEg6N'Ye%+b#ViHhqmeBbUpEX9=mUY+*0I)d'@:6kpE%74L,ZMXtlOruJ %YKm9%W"SGsFf2mlr7Qo*;;5uEHQPt6iYX^5)!Fme$/ak2:`p^p"*"b-P$im'UZL6GGCFC-)>`.&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.g<L(S"1"I=l$*\Ar$Nan<Pll9u%Fq:]2h(A'B_WWt''`"nr %bN_MO@h]rbg3G<oQai)oU3[4YJDcOEVDr`8e?KOEL$u=6=?_CPM=HH;=BFYpE\0Fsn/%(IU%$=UE+sLTVkfMlTt^d!'eFri!,m%% %N!p]+l=H98?su@`V;"#Kn#h_SXe#]RPJ,^[VMSLt)@>0Ih@0H@?@8!j.8kGCR1/QlB<S<M!u$K*4V&!2TR2hXL(D9PlM5!7a\N;d %UtA^3$V;L1:_jF*o.0QNVh]U-`r_=dZ>I^4JiC+/_D\Ki8b:$E9oDYT&ZJH\WId*-CJra!FR$<5&@6p<LrI@[kU[V3i2?dI5BZ.+ %/,HrY$>_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?8dPs<O\uPgR&rbD&;25RZ%8tlfOP<epV#1r4?W<tH$CjK-3AI2h<bq)*p2VIk%;f&Tn:j*.gQGfX003C %V_f+27:3(a`R/jrp*=S.*UC<0>Tu"Ih=V0u[r,]A_<i+]IE*D`fCtTs40iFYj5V%.QF#=o=]V!#W?_+2VL=YBo6#>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:<lDbDibH+m/;]W&A?q6n4OhTjAV>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 %;<i;=ERX@)Rb[)4G4-r`0"G4sIBE!3#1MJACM6o'?1PU<HHjKQ^WW?GC[EHI^bdUWh4h1D*jiU/<=M*qg@!Q%>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$GP5e0Uo<J'`L.9o8:J]DkJZX$N`VY#YbgT"tL^2"DFHfCU7dE(T`StJn1,2+bnV$jk/+M28!;^cu! %];lf0+4Wi2#oiff%TAKo]24rL:&n9lXF4#=(KU<@2'[/1X:&h2!g?.45U-@nqn/VSK7OP0(nN6JQP="DlV#G`U[Y1Umc;n\Ro5;W %"]Un&_O0rj,WNVQ!7->N(1Q.E(n3elYWhusm)#/;3XO"f1Fm1U$dNF=Mb=Vp_CblF6*UA'iA"Q&0nb:,8?.&]#+,:\.qs'l_'Rs6 %.-:4\RkJCJVtr&UKl+E;]$,DJ<icocr=BUW-D_3,,KH7C,1L)&KiZ/r?)S.4d5T]fJ-6+iE!2.W$'mTf;pF5473pj$g\g'"TiN7/ %Dr?u`qd:HUbX:!0/<]AZJ/.'Q=<1B5XIor1$R%]E#<#jqcV'3CT`s)O3kKq`E#f56P6c<16?<on%'#8MQ)XD[,,T!4J'Ja9X-"`> %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&<IW/;n=PSCgWbBYHMA[:=.[Our*ei,mo8KaYGINA4^u0:c9+!_25QQ\RP&=D=F`gI]5#me*^hE3V %LU`fCNKFQYO/YDKX%c9L'0?LZQlg&I2jRp]0d&mp0kdfT*KS=:M^6hQ,\KEo#Wjs+kS-j-@.m>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^</h&->Tur1$j#r[$9qoc94jI@"e?4VO4d3tM6:OBa$2.V7<T)"E$%Sp6`Ih,)/5(%s>$Or/rC]U!_<@)&V=HQ'n`hoOUh@<o %j028'_14Qn'^24Q$%=UF5*`[q_oc5M7(+t0*sJZab5ALJ:cZHu=A@E1^cD:BEibd7G"nugRWV<W_JMBeO/5C;hJlC^SMJFS^^OFg %SD"eE9#/89n.!pKM>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=bd<MZ2RS)6SO-a#\&QB^4ag^N\n&s[_Q:f?b%hV1bnDB!Qu&R0YdtulnrYg+_K>6/q..X"QVk_HG^t?ur=!Lb %Om4A7oJ2L>(rN6qnOghL)=Pt<askAs&iCUVT6Fs=W3S\=0N%E%6G*\Q8qI;X;dE59=P7^R]";^IX"L!KJ:l%C@Tt;Dq8)MC@G^/< %<B++/j3LFYP]StJ4X(aI!aE<XdOH4g$X]IB+E69QFuPVD7T%oTNZ[gRnJfD;iK"]E6if@T!s)oKp*JlN-dR-.p)4Ik##p3qc4L^h %krYgkED)s#?j^(#mq*`H]g;tklm%P"bE?e31q0iC]%:U:QgG'%]b$,lE-UE1?1jG@3oOfj/N5^nra@<CDWe5L>sO>N+FFfA$,9Jc %dcpD_+LC3/$XSO]5D9\78q*_f3W6"VaaDY+9N\%.jcZ;T4@PlO="^Y[-kXkBD1]TsN6i%1AfT",L'4sOrL%=f3h\o>+<Ki<X[@jF %<\L(c:8&W2jk+O0p0Iq;,%j\<Y9ni=ljZ<TSmZL31OT21PsQpMUL<8VY%8$VW0HCj*]VnDf$)9&LbcuM*P8#j&J(LdWH!NQfK^k/ %7MA>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'2uo<f@aP?m@&07aS1`&- %G3j/W:q>Ad`<;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<W5mi*Q'1o)RQdb?:0lPbfF9-r2NEN<>>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:<O2NlW"D2F,OlD2rserj=a?/-M8 %F)(7_7n#Mgks%2I34b%=A&Y(C`9O3FCh&T-Lih04`9IE1-$7c(,1c];TgNhQ'!4u77&b@0=GLkY-4bCU@Yun>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[<FU]p9BnbG7-q^Rm-b6UoYLa41g'hN16n?@EHLI^DPgl?%CS` %:E?d)l<6;\`P\R'fG/_B<Rm>^b!Qt9`=:]%Zn)nC*%R"O+30<RP6'?Uq+Zkk/VNCS>#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(KR3q</qm]S<LKH>gWBt5k2<hqQOj\TV)=U48Ib<EO9i;0t3u1=.gK)n%j4QsPc5gQ'+7dg(R)9_1M)R%QbR)CW %kX7G\&WY[*dA_.95$G+kN32gclspd,V[9qHq^7CI5FCgDWA0G<&YkaLUTO3Jg")1RFM\3'&N#HFb<_LU5X7;DOVG7([.9<\AX:32 %'m^F%88MhU<g@,lcF5f_$k[!\XT8m93d0-&Z(E'DIcDJM!LW0=g`MEPE;CfAmO@i=hLOQpc@7UJ`Hp(fMFbK0=*hML%\=&%^Fu;H %MRcD9D/c3=PErC&Ic0H\)\pr8Fa5SfE%4=NijXQ;.5H0f6HQ^"Kff>)o`(_P:I[AskKnu.W$Ep:1K";c`8Y,F7QE\%,)G<&RXWg^ %g4?oVg9j?aTpaR!ZT6T,Hh_h_miWXO'-MP\MOPQ)Jh%,I@n]*f2b6<BlSWt//Q<\+?$OEoars?Y66unK3eUNVm<Kq=kUE&FheZ?) %`B(g:H70OAKl@U$X:n?an5^Y7gu.P"r>A<=o_6_[NgYaj@h&Cb_j(:7Gt$P^40CLnGfNf]`K$RtmRd&U<XL-+P2+]fBagQrk3ba: %#2WQ(\69M"EDp0"E#K5@P\4'Ep`[?qSuoATdfOf<[:q@4ME,/^WJa6]-:2cX"Wr?<a<\bO0uZ%>Pa&*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=Xr2Z<Vr.U_.U,Z^(YkVCIH)&;Yli0qKZDH%i[n=0/ZHq;af@%+>6@-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 %o<JI=gC0KpRZ985DbhhN:E2;^9H9hE6UXPfbi!iY,[;+K>Thc0-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><h?C2_N3D9_,(2Y8%buMR(>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)<HEHSC.P01Wk[#9$]EbRjM3JB+464R/6@+Jk]Wb72VMABfj<>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%<mZ=$0:B/eKqFMkA7S:75^D7T!:b\ch8CjEk_uj.D]0s>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(S9qg<i:/4s:I8Zs-pI:H]%Lare]O=tn(]Zt.n2lfr<-'s'm:SEOr*I@:T&fYZ`.Z??\iDk:Gu@09#lF[/N?#`% %e?F#-ZXD![Gdf&I$l7BmU^W-3JAC)"MB6"AoXoMB"=g*L9gQdVE$(t<OQT/`ktSh08>KP(4oc%an6cj*Q3H6GKIgoe(`B`+aaf-j %>onesp==d)<r)_ROaN6BFR:G>!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</BJnU-5YSjtV*dAZ#1]CDl&9^A_k>+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<O972h!Y#ZV<#gcDb*Jd,pB=6fecE<\MF/_0QcZ;V'npd2mO.D,)E@ac))*?BXb!M-)Z,Kai5F] %EEVod9UIPdB"nPheVM2NY=.Mg0MmapP""I",e!lc?r=%LWGEKIbpJ\aRl`\04CjBF].EM[K"Wk8"N/X?f$r'F6NNVH!kUbkYa;(s %*%N+c;L-BXo5C').efoZ^8bMc=n]&UC_6h7<J:e$%C<B0E@i<'?j"b_3!q<DJ<5Wu^JX3]S7.<u$Dqt><B\LO9puDF"ns_GI[\Ad %R5QNr-IIMJ#f)OH3iRfSW.Ic%T>&S=MpD'=ihG69Mn,\SGhO$[j:pMuRA+XD<HeE-Qpk!50VE_s0gB>E'Bp*47nse3>$%)5+rEb. %jd<''Gc1-n4KMql.QWm?r.*\X.=Ob*d)f-@Kg6Q._!FFg&s.>1#@0,ErAi9r:5)9sA0Y5,Sdr<Tf\BkUnL$2J!b''FW/WXO.9gt> %5mJL41_k/8r2`?4mYe&ZVZu2oMdr3Fc]s%-T:e_E,\g5Ra>d:S0%\WU;,WY%'kF>iP6&CJoibA^aqc$bK\e'R[<TK//@ODONC)Pt %gtAbP`_ib'pqbV<m,#0oOAo_XPo6D)QRMXb9MMuI+OaG8`]9`DZg"L2F"jRD2!*:1!^os%5h4]o],2g'N*.T!p-!oL_N[Y-qnXs7 %rQ)%&,%VX8CC#iPd3gZ*I$`N+[,X?@Z@pbZR\iI*UT-^D1q\_=R9gK^36tTuc;pW`i?l]T`;-)O#dTOYpn<`[`!h!4.PCg2M=I+( %rG3PR+6X"#"OIb3arepb\7=Uk]Y5-iRDp/qc,=];4kGs]P1_8;qYs,5ER9IJ?tpa!@dr8(Rqm'GDep>MYb"T_W[m@0S1f=]Hn'Kb %<Cr]g!k(ND+JMGKH8=jn=:MJPd!,gJ/QF6OA6;h;fh9CH%!p(OS!"dSWsX7sBOr7EB15Dp"Ek2F%l'PO0q=:iW!rYXg82Y=,Jj`, %%:<o%$3lBN$-oY[7/8/72Bk8D,Uh#[pe<`,\J82I5i.'<Kc`p1d]ofY*nph4^*DV/S))6<l9]X>h?Q;o=-U-@BG'jgOts1U(:gN/ %EM%,9!5&r0AMB4`1,p-*,7`1$-fbuc&a,Sbehlo@e*fP;B1(-nNRFqfLh$<j-/su-#*-R,c;[eK=_B])+>m]QSCipd<igFu&GhW& %VV/:=9=l(4+3Meo5L-W6*t5WQX`@J4ZIF4\$fFK&i=K4+\.k)nKnshlrfamF],>"<#`[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)-<q@h3]Z;"pXL14'Mg<BU+c@n).:gl4b %/pYVa+aL!$j0>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?jDb<W*A7KXja!\eB_t@&-LdIo<mfD;!k5s]jt!XWGRr#6QhO),8SW9fnJg4:U`HQ-n4#,oWOY8W.k$6+L\)Qh %Z(>K>U(rD(-as-l%l(\LR9a]%;<XF>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%<a3cYN(ufDZWb#&#QkOu+dVg_RpgCJ7/,OU`b>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!]BI<CDO2/V]E,LuBE0o-E_.0<`J3]"^^9!^`tC2*'PD48rFeI>DO=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<Y+>]ncTc"=Gs#ua5Fc`s4gjp(K1bhO,0TKY:G0u>6\.Hu0]S_9OJ@BQB0E[!^]I)'mMcJ?7JZ929JfbO<%U!pN+!;>DPc&9 %WmT6^4j9Z&j<o&Ean81$48(QJC)c"!i'0os'0X+cYGRh-/7mIu,e)UY$Y+_F%3\20V/RjkYPt<]V>"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,</FK&>cj#^m)bbL+8VLuXp,@4IPLe)ZX`'[Y5^d&!5OYN-E"[FXZ%Nb>%Z&Uu2@3IWf:<i4b;J2j*6Zt"Ln+OK"+r5Sm;@3Iq %CLhac+a\5?kU8Z'ck#L+S^fp53Vd>s2Jj*%;s8t6:T9!uc>iK@"or&rSG19W,3fT6!-ss68R7)To),a<Has&SH0HImNTq=IU0h1D %8+_)Nc$PYpLe!e-!b=Mh4W9BTXD5'@Fha:oU]4qM?T$FPGcSf<>,[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\/&B<E+RVoXb %fJGRjK#;D:rJV"WF5CH+`e($?J233IY'Gep<u3rAmVPL\du39!UbEEU0"Z%j)b$?^E4G<U62)90CLW234VlFZ`9=u$esu3i'!!FE %_r!jt7rh*j2gCR6SqWQ\Isc_X!qM9JGcq_C7TTSDS<)2EK_`5t#A,%[5\1mEk')Hl1fJC16U^k`6+W-]rpn@(]Z>JN31M\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<h2U"3<Yfs3I>/Z7]bi0+W'U=Z/YrFn^["E-SVR`o"cP!`),C@SO<d5hlqc1,!&h_@.K* %g%a+ONPlqHR)c''?aY]X2r-o$"M+oD/4_?f+_AbZrpB+)KP:A]nXoF%5Q;Olrknd3@r$4S8?3(>`B]Smr:/k@JO?SG0+-'@8:T9q %X'gn,?-e`2/_ih*C[W&,koF*"=u5NNdaV+7.\_[<r@=h:p%Co+:sRJ<7VMu*m1BB\],i/ThGK'&le(a;.MUN0gXA6sA=spcJ^FQp %M_9Nd\iWJM20o+FAhbC48"QAar+DCS;nT$H=cVpD@1["(N)"YPRnfD>mj8+e!q-omkp8PJ92u+02C%+%47F/1B2lDP(ld?>185<? %(=l&kXj?$?mIoIKiY"f2AeT;:-V!/W:FL9?\mHc+FHo6+oVg?k@DH-a+#`QARnn9'>N9EU*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-@<T%E&58O12j1ZEi_U/aY"<5i:<=.;jS"Esc$G]ZX6Ff+eF6-?CRr$M$2E<'OmdfaG^pO:B^_1t;s %5\X<Q_JgKS9q>[+MGU)$<6S/=g7%/O*5W>D4I.fV3#bU/#hUFZb.s_pm$PB\;9,T??L@@XW@VTO\Vj<O]H*M?f$8BF!beU\]=n/C %_XTGJ1,D[_C:#g,ap:@)QV-=CLAIX>A-"L-f+#sqp&7rul1ZPU"D]Bcb-+'C;;<,frlB>2\2!JFdnm9eF2Hpp1fhR!ZR2YeBi1*C %c#9("ZWh5@<QO:a5g"&J<-$lUk@]6?G4D?Z,@YJf7[(#'1_JJ\`;;,u+h_5OQ2`mSZgSf2opO+)0MB:ndgob]1.P!LC-1?F+="<2 %OBR68D;A%+,P.?Pd\>@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[//Si<N$<\9$kt-`![1Kn;GojCbTS@]%NNXU%(B[PA-mJX-5q0J %4tW^D7SRn-4:BV"4I`/'KR&@oD8s#;BXY5QOsMTG,CGismf\(j#?\r&;M+:E(XS)+2T=Z$FaQ_!;*i'5)IoP<76uS3e\T/]L@/`, %jir7'L_V*RO4D@fN-4YpXOo-`a6m_\^U&HL,cPg6U1"$.`io[BqkPuSnoh4P(k^4+T;1fJoe.tL-e4\=O/a^b7?8o+iRMu`J+#/h %=Edp3#4MaRp$e;umuC)eE&TYMgffk_U!*_V/_d-;9^i3BW[ZUo'#8FZAXR.pn<Z)KZRD^OS*HlW`#)/a`j.)%77o_M.P4@1:WD$8 %U'91u\+SdEk+S6inF$?nlUaOH32,qZP!\9V64/"PZ`!e]V+!`+KfV2c@1&0pPJ$F9,q:3q-6gC5W`TIAs5!*`[]_pKD9n;d!;2J1 %9$1(5X6+%YJ<eg<NP,g3mAI0^:N,mApV%:m5BYfaU1p\b'*6U]UP"s?^SQoi#jl=K8&1_8TnbNcIQ8+X/sUI&,1k:MFrr+kF;O2n %iINs!N$0K:#>K+cXoQ3[lOK5(JQ?5@#;DYRXbZ^CZJ'u,5un^(`9jPI-&uRoP5qZQ-m:ha+mjeaZ]gMs;Y4dJo_H.V/*3Vke>q`G %<PrCi840")PRL_<[@)tV`Pt;f6tEI,agmLPT/n35YPFX5@D!<\ne'CI*b4#9RGWfqg2-*0mDG.6^@'mKKg^m$+-'.a$83ouOI?(O %mgoL&7J5083K0j-IHd2AYf`R>,!Nd(a/:I@=^VEQCmT4)"TJH])oiTf4,8.3l]Y(<dJc_H"#YAIZT3W#C9d;n`;T/ucl]IK7R07[ %_"]u#8KI(7]bY&(&.VPljC9`&E&VW*LH9b+pk@/n_`<=d*JV%jah]1nE;:#rLqE6`8T+dpO;;9ZrB4Ks'T6G6$eM=B0mtUIPYo/t %**(hqp0!7=!lWaOO0[M])'R8YA];P0-#aP!g*<mZ6A^oCa$oK2=l>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&<BjrH05C083XF8^($Ts@mgg-173g5+,K$+LpehjlQX[>jrB'"sM^YqDpi<^B,#iP]oI$-48nJ.b %K6+^+BnpkS/2ae4\Vd'UrJ.=`%>?q^;8[d.6A\>#ii>O[E@(1F4XO$7<qJ2*>E^%;%$;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'<m<Gdd69f'O[OipL]Hl8PM_r&_JF"de5 %A/N>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'4<qCM'B0m)X'*'a+!FPWEj;*;O0I+V[#CFVMkA.ANY6.sAb1ZO;.s,!Lg>mCcP_Ak#M,Ablt_h7i%m_O[2<N!fLO$a@o %CVY%,(X_!+a/@O8c!3KApiJ@#iH4q^;"HpDYfjNY;\p#hpaW1M-jHKu*nkA^Et`(&C4V&!Ldn+A+_\73,V+jhd86ll";lo!8#NT^ %<@'CK%7554pa*=`4[nf#.F5"c0.NN$Pb>*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@<m:)"r?s`&<>(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^<B/@]QS]01\Wb@a1]7Jqh]-lU.Q*BilS3aAHk(>p@!)$GCDYJ!GWK'E+gD$@\=j!W^8+k3&./M2G.Vlkdh*I0r91e %^_QW^7gsP<+jR:C<W'b_pEBLo']4Od).#a$iXnOo.RmTbS:L9Ncls,Mi"'[!W,b83fKkI-nWOTgpibHqkMNoncD2$h$I0MVC&p*_ %LDj&pP'.9lM_8@df_$EO@Ma&G4Z"fP3BiGbAhip7%JpVr7@m5bm",?],3SSg30X4-MQPDBaq!UfDR1[SAd"W:a[h%61+,oC[1YiA %;eilMMhZ5_,#"nhaQ/9C`-%@EYjXsU6fSV5Ha75IH3q>1gGebYD$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`<XO$ZZL;]/hnaI,P" %g1DE.8P289:ntghhnIq6l.'*]mSKq!CB$\@i-,\d1mJ2_pnD\>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:<eV"!`C-\;#n^h8'' %TFSO:R^s&9,7hfC?V*oXb0?pSlUjsWPpH27CfT+dAa<Wn$/+`H`'"9uVleZV9-Esi=A2o;'e]LS`BaBa"q2i\L5Q(bnp=I@LhO+! %ORn9]Yi7/LC#:bXoPI2aE;i\GEN1"K,hnp(W(L'B07P^7De=mk'5^ha0nH6`FBJUgq`rj%S0qrG3.Jl9X+=-+l![;BVh^[%;+./S %73NgA;C.Dc!N>>SB:01V*B4DMY-&&g#m_=&$G%e$B9S>J\tLk-m3.I83XO0uM\*Fg4`cDk$ftic9J0;Ua=g<ahQ\sr<:Q$3Y0,&( %r@DEVoj<4W3<ej=>XdKnFEj7Jf)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<ZfF<) %MRa`QJuV-L-n9,!5NBe-Q_7SppDURCe]WY,7D%(da8pZ185c&5=B$>,:i^T#a=hI=l*W['$4k/IBW%)MFfcN^B^YAiGB7h8'F<d` %;6EAAi".&sU;G!I"b]rghQ/<@EXhR:(O_H@mMN^)%NtZSg#@fk<7Xo8&`MYN\S)'BQ6qdZ_6Lqe0&;2Vb(+uUNW=k8_`M9X\gp<m %C8LU(Lu(=+QXL$71B'14fHKJU8]/][5.$6=ijh$KkE29pq"@b^f0oVV*^<BPG)/]^qf_ksb3TP/b]=KF204<nR@:+jZYAR!qq\ES %,,nMg0blnbSLZ>8Y6)'](_]7nR;+/"eN\<WU7Q%5<WB8!jE[haWrQ"tBO8&@&U_<mE7$6WRr+SUaeUPX>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@QV<I=kD>UujB3"+8lr$B3R$*caS(n:d6^dWmgTaRcCAjle7M_n"RCNSp;qXQoMutps %8FSKVZ.b<I&rV2>jM7Hfe_KadIk,$saWmMJ:mZs?7tb(hl:8rOJb6`Je]Gq&i`r'Ano'-K6=Z-_S1`\nFRLSThX<YH%Orm]=O"kr %EOc.3O2EYDA)Ijt-CE%s_\g9_0BGf\4>iD`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<Z_VVFm"\5fi0 %>/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)qU<Q@BHuW6r-'_PHTd;Z[AM+ZC7!lU^&Jd5ps3#QJ)>CP %*)gh\;=fmqAP*:?M:jn-QX+FRUb:b2d@[:U6$KZ[<BdQF.[YD46L3FH%fbqO`bb(ZP.^3!A)j;hc#p]-YKJhRTOGdPrm3O6J+VRb %&k:"[+^;D>hdhJ-8">(Jb&/0I,n0-g"R^(nQN&;u@e%Z8ZCtUr\iobPH69.i6Xsmb<b!bCIVgRI=1pI7@jn<oXicC87;Yu%]@sr[ %>cB;]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)c9R<aXNSKa[IGq&Ka;r.&R:K$pge[l$^Vo;R#=mQM*Qj,4D9@HWdl2J0THL?r$2<L&I[V:^m'C'E< %`B<T>mI338rFBK=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&<gWD5B>\gsEPFqZ95/H4D)Oj]HA^UfdW1e-m3*e)c5CsSq,]/U1#C=9YL]ns9(TsjSU[SLDJ`4>8g %LFL<a[L20"(9sUnpE0;0?Ip_CM0;,VE2E(gIE?stD_#$j5IYQWP0o1,L!<mof+M.Ek.H+fnTDIY'Jm)m6.9>qHB#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<(<!9B:6YSHe.W+^!a52Ae1h5Eoq[r7=h.m84rQ3tL)IS.>bV,2l,h<?#dIX38kdLiE %3.'#&J0;*@kH%D6]611#5;/Zh(^68aGA[=oXfrgL@\`DRmDA5_G4<lWp+\Z!qqoDe"Vms$IU3`H0q2psg&6+rZFd8`]9:WQNKn_E %=391'hLWHAn@jUOpU\A\_K_tsg5FTlV!$Jr(1ZYLf6(X4FZdc((,UU]Cm]5-)np*HPp?JFk'.;m-22e3[JSgiF&NIOD(JE+9_<b> %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_<uP,pr9G]pdZ@!b=?Hq*J&'8!#IH;tA:Mgp6t %`*$nY^e-j#<E1!u/3#99e7/\Ve:rRW3Q!7FK*77&<-<F5,ndp3`=o2t6aN4Khjc@81tNVg]nnqj<lK<or7RudfkfSB1q4%B^(=Q, %2cmSKVB7'MR>=&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+<h*=+r;H4nUJ*EgYK?06U&qTt"Am>:?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"ELR0G<cj))shTZnhfgM*hmtLTQ=b]p:T8\8!<`%eMd[!*pLg2O:)T.TVtCLC,>712'(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<GN551GN*3:_E$pC24&p6lihWQ#s2,CPi[?:@EL"N07F`J#LEU4&d46lbGG %o4abS9FJCW,/&&">/;am;@aeoIi*TI\"Q'Fkp7=P[pZB!)3/I]A#h5+QaJc87P1SE[85@B?h!Hb6sg>sFj\_Z<t]EDP!X][oMMhk %onn4(a>T^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'/)1L<HQb-ra?r-[NRCd[e%@Y1f_Hqg32:&rbs`6X1mlm6k*u5XKfQ&$^Gh: %X4AAu2YcNKoBd>WIJ4*_(16H`&LK1$o2`64q5SId'`t_kp*%-"gp6Z`$u9'&\?8&$5a:9/[O9':DNu#h$/:&SF'Z3Q&=jl7D`6I$ %dF3<SU4ZAK^Dd_pL(2_gp'nb@`XH1_Z#5/fPEEVb?0?+rW-AeH@7OI_L%!W)?ag^bJS75k^L+2[eEq^f#=ae9?15gHoF:"#hpJm$ %L>ZduJPFsukIsq2TrB^&D_'.:EnR)^,eo_(iJf\+4S/G6euo3OGG:AM]5*A5KI@5\``*&R-!q6U4iL0Dh/>nli,T8<G;nf8#@b#+ %?$XdsXoJNM7M3cX599G$5sr9(K%=aCV$Z[/E2u=.cSB`RTqbkI,+2L;nho"%<spbEEYFXC2r2L7kA6!hDB*fQn)oGYC&?DI1?>o' %&Q>fTr&Y`sq"aa^r"2WQgN3rZO5+YP_/D]4AXeRH$sqUk:#H@T61j<W#%Pn\Y6NYGS$D&#5Hi[p@/)A6(9^pq1"C?)S`TZcq[GkG %rM`j>ep3#*YGnJLU2(^6`g1dLMcuB,]oBS\-!8NB^M7Umdo^)ubOlnGIX<O[gR2-rF^=V@h4Df5&Pjp!c,U%PBZ"lIat`at`\'94 %GQ.;MIUDW%Jp_!3L9+Bjqu3SVSM?%5YB=SlD+2E7cVH#e2e5L9^"r+#4KfI/Ji5B*S"?hmqp\5dg,??e_4%M]qae1Nj*1&pgJr*( %T2bWGoniFt%5!\)QIU,K>C<:7\ZJt^2A""h+.$5e<GufVH*!ihaa(Vl[nlVmJlP*RG#)R_%_C!(c(n%88fu+,QT]HrC6#(FR^"6\ %['h+odeD!94?YLWr^XV8R=q\2SBBaNRqRK971TE1cqYi!qW$@d[WMjGajEUM.C(bMg'?$a/$#L7\3IW3-?6igENaN]7Jj7iAAR:G %AC=$\,r_EI8;tqkKc1QUE7gkRhK$:_ZV\]rg!'P$c-8PpVDHRbU#$me;JgA3V[6*+[0L+\:R+L[2_l[83^d:q^OFFEHXZTANF-G3 %XOOJ&2UWqOg\K;35Nr"Ark!<m(aaL@+c&ME+O-qX^o$rO$cN^)ITM2dS>^n;VY2.^Jfh\WcRD;ldC9No+%&/l,W/YSO5:_/B*K.H %#%j8sS\u$'8%:VH?Al>^Ee03Y_.n=C<kS4NN1s_j>`&5]6\<EeE1^[V9tSt[=s\J?C%_"q]$t`79]fs!bg[&D)fe5Ng&B?KJE->= %1pG"UA0?b9^P!94.eB5qD*&[BfW?*B&)m,<Da]J/a^ld?>Beki)LDe]`W`1(FunnGe7QAJFo*.[mJ$1u`?<iD-Is"*&T>G@CTMd6 %\n^:C\k".#RH`[OoR-UTimuf_A:+6<UdBT7D*j`g>DVf2%J&_hJ_h3GF/8;`Bn>ehaRahjY'%S0QT+YC45`3M3HeBehW!'/b.:SJ %FZW-:$PUJ#(qG0ENTlXN!_rQ?\)9RH`4P<K)tm:d"b5g2e/RNSFS5NRhXK4)'<rNrgjP#bY2A-=SSu9:fnl_5'EHD`!r\)Ga)tP$ %s'6s&s7Fs$b`$\ZlT.BTlW.K)hBd*J+AJtV!3Qeuep+l<qqUtR]M4(^h_f=N&`PUm7Q7D48p1-[RZTRX0P:oNcJR51(en<-D;E5' %<kKch;p,@KlUK+KZ;"d%1[jYsVM:G`p+ndOOuHCq'h%BUQ!1Bbl=`Wn*q$T[QQEFY:7]JqR"J=*<4?d?b"?.RdcG7tI+R8m!5TnY %>GNnJ*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<Ii-]NVI1`\k3F'uOeR(A2q8_rKkPk1XNTf/E^^$bZikT"CJBg\ZE'&V)3^Us[gYMtWfGq0Cch %@MS,"gFHk@4;NT7XOJg-inYXZK'IPJK"<H3l`9AXPlG$.d^d4DBaYO"*8d@OQ;75sEelOBANpX,bI]jpm3lo,g'i59Kg]0^S+g=' %/*HKclEi([dZ8#im96&]Gil:8Ba#ndBgQXD@$+Q$>s61>[OU^0dgb-S]0k]1#pddlgO+^HG/qe3huEDI'pi3T`PuGl=50*ej1a31 %(u+^4kGPQOqHMSD`%h],s.O5&\4SiQ_9$\hk8F'>]BPH]kfr^G^LT.S\ScSD&0>>T6?EU)lY5"&i;/PQf>uSblU+.KV<l500jREu %mWIFLDX[(G?!'D)6]_&GeY4=8\nF3XA)e?GCN>NPQFX/>&M`MmgKJ:umd7uFY5TKW#)-<'_WJLVIN4fc+^0H&4joaZ\`s,+VgqFV %r1W;uUiJn?[6N8Cf8668',#_Vg.VE=o8t=>=$8T"V&!Rs6lW<kY>=<iS=?a"_X:kqrO1=55!AP51W2GAl>DFa>Z>3pH,Eu$TL0Gt %_nh"c0>STJ-E6aCPq>tSlqR0o:sLtqH4>.3.s><'7^Rn5f`VqbJ%B%q$3:CDl'd/%p2[>s8OF?<G$H0e+VoM09#3HE%MLDUkQcBi %#Ol-/?HASD8j_U!QudIbe*[T77""lMP3uj`1*IpbWuq#tM!1O]NB"I$d`B`I$DVjI>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<f1V6@[&M+SQ=D1nF%hA'r\N8)N %W7nf'NnROCl2E#\9m!q+DFI>&goB*-4;JMdL`hi#]h^9`F9Mk33Q_HV-hS%D&$l9HeotbZEG4,RP?dcK=1)36Yfs8dQjllmRTQ"@ %QA7MlN.l5B-Q<NMVB_[OYZm"L^8^*G3`THTg!V.g1d)ec(,_WflaH\H<d4>^8IV*=PeW`J,k^+5:l#BAGKrs;RWp`@X(6^^o'';+ %+-bMUp!dQ&V_]0g#ouO1Y`Y$M:72qCd_#?6<d1;;C]i,=A;3s@JOsJ.VO@]*kt0D"$k"rKhTk?(jK;K/!+jVO557jEom*q,W$ZuK %$q,TY;BPQn#n>0YaPgaSR)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!<PTGgii!HG32W,#@!!8A"OJ>([%igim2o=Q!Y5%NNTTI/t@6,W9?AXEsL %PV`=P]/u=t<^$)CUPK-ohi:TBA.POuP<n]g[OR6C]/>-)*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-*<A_c<B%E$'+/c]API"*_1'>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/$<mPA,#4dr\h<=r!.%QJ:#'q-dFW^TQ+GfQ30,N;&s()AfFl<DQf8E-X%GdY*pG9 %+T)]ab\eMn?)!Fs84[ZV42b2P2`<G/*5S\r&quIcoI@l"@lJ2i][\1"M7:WI(j*)oL_H7(1^.b;(/rF1oG0+hVuR<bpGppjYQ+OR %!PB"9>\R^_r2pQC\Lh\0PQh7^':s:YqeF.kE=V1L_Er0JC$N^J!69p^^&`[o8_0)gE%4$S.N.M2"M$dS]<EN]bk,?_18if<F?=a: %Amn)&Po"#l+0?sXeqXdYipBa69ba/.BSpX>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)QN<DJ %]8g=c5;+>QKI%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$'$lBUOtY<tp8*O+\0m_[Wg+j4KtqkeWb%hac[%sI6uq&DW8TqdHUS2HcJlI6l7M8^&4CS:2Zq+[;[T#^%j*/ko:Pi%9b %m$USkk_p&Y\i4.p'$)5Zq`:/Q%F[q929RrLZmq9-eW;9jPEiEM)C3M.)Vr+O=Et6>N5jBB`,t*2:\;*U!DPA9EU<AUbU+?144)%: %U59se.O@S2U%]&Rm*kG9V.s&c>$#E5_1.N2R4BQ/?k$tR3dDDO?*p@!>HTm.f?IV`G,%e:V2X`:6sgQ[eT"&NMEiD5X/^C?a%-?N %^Fn,P3U#BaD^_\0SYl=OlVT<hC`s3nTJmo!8?=a^6epbrL".-#iiG@j@o?+%GgTfrPERE\;(loMTR7CXG\dt0>(e'!\;4]m=efT# %AGH4ZIT_=4bme.6-QWOV-K]N@]1lnVWh5mT)A>D(CB<u7";W+#Tl;R3!M%eoDG%be@Y]NiEHl]BMpJsP60*fC!;G'7o,lSZ5%'%W %nnsL:jCr>uGs![?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-<O0J[_U0n\MM'^;^,!P+0 %!aIT_o/pCgRlS-iG(L1^W&I,""JBj)klV7?hS.AFk,?CqMf5jt@W5t.+,G=@os5$C)o!7*FScJ_DE+-ei=+<=#\j($/57$]V;,[( %!M1#F)B)!f4qH-bY(;C:DAs2hB>9;%-sF0>.(^5(6]WL@*%]TOS/4&7PE=]D$r&.dlN4^7[e^.`+I^)(je\P[o%M+[\ETNrl.?,4 %MN8TAYrMI&ot0Os*5<r)-<(+o$"]DFWOlaXV,r2"]/!*HY@R)9&TG`\Ts9D_MT,Wnh'`$0<T5@_L>2u[(/heN>c^U(i[=:X=98O# %!"/h>$Y:>/>?e[)qNPWL]]KB>;7b`KRs5n#>qlO#4`FB1p5lda4lP960/Gt4.W,AEfHERa2S=_t+5bhh!>pr<gJf_NAD)OJAYT$_ %hI8&Zrd0tb\ZkMm7EV?o0G@P>2A.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#;*><GR?7E'-3MG#l)^jRtQecVOGfj,Z"3gdJW@L;@aLWMnE!_G'e[&aa_ %[BZ_2:eRQ#lfOdE^P`0]qql@<<Hc>S['E96@pFq2)ll@R9/.<!ic@k+W_b=(gdEs]@lMQj8LbgS%-6!l9Z@*a.W1Gb-Y8`FS([<P %PDmAfE)Hb`h"0X;1o&*qIBBd5ATZ`,<_cHF`e*@Id/1AB5in'ik,u6T&Ra?R/JjG`S,Iee>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><#)LUGE8DR<f?RtMIV1!J&fZ.%eK&bN6h*4R70/r3Y/V*/9=$-:h'DNNsrDE+dL629\ %6CX^',?"XlH>qC02'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<EoE]PU8VlO8L><r4H/!qQ)u8Kbqh6nEm+OdS3D3U*JolCU;N/= %Ih$2.B?Y+?i1,*\C+3oOTqajm;7Mf5YS4)kGN#.lI]Q/$a*ss<!g3lhKau.+i8[NMYD'iVaG[YQ;C8q_VNXd"j%PKX!:^^\4%H6> %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:\<AK7]HsK.0P'^L,*h\Q^J.k)RPNgXd&"<feWC^dj#!jGW1Sih5UQ8n9KTo^lJLr+C"l:Wp %PVIqg6(X)q0i.jKJ2uU1g!5k_#'uEVC//2;YSt)eb6*?H*!X]\9J/Pdj5XB%Mjk95#dH+SKMrCP>`f#(lGc0Gm<__mjZt?9GNd*J %ee$X"'`&DNLrKjb1D86j@A1tSR0$9a)%DktE&IAj$sYM^>0WlU#U9FIcENhSoo+i?eH&cmA6Qo[9PJ-kT!@r2K<Rsl:7`oVq1"UJ %bIM%.j[&EXN)`?teAR)s?Yl!@^&hG(1R$QO,D6sE5Ead):qTa"SI!Vq4TQCeF=^$VL3"+sW_Tr5`g1C"%qJ271/?9qFdag$d!"=j %i3XqscK%U94T?kh-DD_%8F=9;6mW>kA2m6u@6&LfI&*!4>9aV7OKrXg1;G1)P&juh4U_g0/Kc<Rs+>iTmd"_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.J<oI>C%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?kXOVVA<Po(IE:<?0QmO]Z3=I_bDA,*hIr6uNl3^#UeaagBaW]a;SH3pjR$1]C?9#gSk5UCmod\tM$e\@A"C_C@]:YCVmZ %/u,\dmmlP17r,0K(CXL5d!R`s[2I-,T#>cFOWNQLprU+>l.A<TLW4S/%CmUP?L.<o:DM6]b4lAn<*A<9eRNb#D-g`<\uAb6/`%1S %Bb'fa[:@LkD(7"4U9Wji^"t<Rr;qY-T*/'D?`n=>95N5G++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_<Ibf=eM%SV9^A)EULd`>/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(u1<Lc*^!3m@D`m@X,H(/h"iNQkCF"ePG^Lf.]ndm9A;6:(Z,Feo3:B2lV %k_#B=7S!`9%&Q9I!Bc]pq5]22n_@jiZn#?cZ5h8r&uE3*.mR+&*FSI#PVj)u7X^0oYQ>GEAVXj3[^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<nPfs%B3(N19\J9R`/\,SK*(Zo:`oaZ2-Fi'#qd3='cUZA&3kVj(2l %8Q5b189$=1Yqh2eN:.76&'m2@@0,gUJUo:_^=iinQD_!F"V4,^H/jmQ.VP9NIQcNR^><*^1mQ9=pO3`R9GL;WMG`#Z:uSR5^c"o9 %<c$";aI-a/G_eO5446-<%6b9g[9uduMY+e;!PED%.MR#TQtM^jV#9Ik3aSNlb!mj+PL=_ofg?P$;fi(P:LMJ&J*!)8n15tmPmY3q %>81u:,)VE#GUA9di'!1IVPk+@F$1L!LCla*[03V<RYX12WpR@!E,DJq5Cg-pm+R@!4=It?hZI#:PH[T;ZtW:cO&T6e`2YNcB'rkO %D=Dk]WWjU#b.mCSJPsuqQrMCM7LB7)!fagaWPdt"H%sEK3>HVRjoc7G.;B"30Z3&R/I`Gbn!!<.0Jr&sZ0ke%CQfO(SI[D9d7`-B %.c9Tho<6f#2-A%#h<(oNK(e`V.\j,Q]1OIefCbr?<Yo\8e&Z&"f?p0=\&i]8Ji7;"m0hJQ[0W=7IggNE_C#'-$L0#4?oHgQ@CJt9 %/lpaNPAjls8.;Fr"Bpa!fA4M*4%UkCn?`$Pm`7aQ"Q42$<PVEOQ!8GgL,jCp5gf:8=%4Xp=^pAmfLEo^?Y.22*A<\%Aco`q`o`;P %Sn/+d][C.K\a;!(=ZBlXUo'DIG9E<L2YP)396f+*\mjWO]Bh6NI;j3"kQ;05J9^pTrT0aQi,hJo43sq.%$pkUc=#;7GP/3._YJM8 %'6:,/TtVuO\e[*4m^S1[``#P=d=jM'@0*ZW0)*\lkAq.HVq5&T3E0OH.51T'SkZub$K`g'j)siV5QDe^3DNRbdigu:#['C<b=BbI %:p-BraZ3i<=cnSp%MM"]SH[0tY/1'Yq)U`oD%#u,c;W@^)R8XnjjTaq5-,>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*<JhAoo,ELBYKQG5=+uDM^UsI!VCBO6bkbJH%'2%so9)hPn5n?[]d_/-KO%2S$m$&1IcXaX=!JgJ)LA1O!:6-9"@?9sB6Z %Aa>`!^ZZKU:'CL*CQ<to#tA$AUG/09"JMDQZb^\Z_H@lJQ`7Xh=._W&B9g+lABAO2:?*E9WcCf/EhfilP6/^*NosR?W"naZ`FFeV %=G2\s/OJ#.-a\lBR]4Igf#AL\!FD*FdU!#4pO7C-fm,[JOhPWB7'*F)B/R)ih7@DJK2Kt\M95^A\,I7IBtA%6k@QM)mu5!=oN'_0 %ldH<,Ap9I):N']hm6#<)m[ZBTY4c]+G9]>uM,.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%gA<nS!3-6[C?-N%-T0PuJi\o1Pqj8,R.@a?QsMn2I8/=f<^;;:%R5hfk&l<VQJR\]XRs.0>KsmJ %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)<W:^Y(:"9S+^hAr0^;M72Y\/!n*l-X0 %dt>VD'QA.OmL*V5IIIepNUC$#1\!3$aCC17i/j]obCU1nK8(]JpnRQIh>-$Je`.cnb4*$J7?0ia-?&6*KV6cO@rLrX2_HReWE0p/ %7"+b<R+'M<icuT8qM6<^H1(o'UN$2b6f7SKUm6@-`5D+W5gk77k_k3,P!.i?*i!icr_F$0-.BL#N<^)n5.)(T]8<]<jO%BJA$FZJ %&.n%kh`.<)@OssIU\b6HDYUMl[1kbDKDBn-0^XMY^+%*N&PipSf-Uo2`\i#<0RrT9Z+Hq$K.#Wm8Y]_=0W:u6T[rXD=QT0_BjEC3 %CH8(.QC5]7M:m"Rfr-h_i"lL,)tJ@NVGf`f^)<qM&Lcc1H'U!QU>V"-'o!g5*1&Eag7?38Zl9o/Nf\l&=eX0U6,@8$_qK*-+YhtZ %0b`p-?@CdB$S(K2COqhk(RW-0C03Z55>Np5hdR!'Z5=I3;e_63"<BO=a*gD*Na^??ln=)ZT"=k)<EmYO\^C1+7(;qFLAWUAF*%qY %\ctgM*X!&HZR:?-F\+]N]DHc#^1cLKX*3,#1`F<M2MscaS=V\MTA_0MA`YLAG^Ke`^3eb`f)s]SJM)@cs4$Yo)'17.R2W.QT5fYX %r:Ji_0:UJS@4Q^J1@[M^cP4A:TP4iF]--;-%c9'++'J>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!(t1<T0GDcaddFfR\hL2^.W8!Q'hT)u-(bGSg3qKO'lE7_l.H@*/&ZE7]^N %H5K,hAbrr^K;sRp"EoS2/X(6:Wm?PpH;!0/?r\Rfh6rp*Q[1e;;WgupA+5`+QIHLKJ)?K^B?7o<3hDQn<YWd,@(`FI'Ckj-e;.5b %[&q.i'>hs_O*=4egs[<YQu(JXHu0h2E79<X<sX"Z3YimDAF0[:M1cM620)mpNThd)9/Btb(P?m,1"\AEKUMjBc5KUfaW&AJZGY!Q %;i,d"miZ>1U(#D!c4_&d3UL)*&qU]Th&>@n;D)h4hBO^FfO/C)c`?hmH%rc:(>%+oXi(!`i(+Z6bAYsQm'TR+eq^Yo[0A</Vp<0Z %5B$#!CKls,LODJe6bNaJ$(k,P0D6')j9HXYYT]cU&r%Pd%S<&mFphu-6dX_;7Dp#fF76M[M3?'M7X(C:2-8(nH&^"'&[A5frR2C; %..e`I%aJntC$:[8D26K[fqM!V&dMk#Yo_ZZ_/&!=:-d*E"jIcD4Pl?5q+pJ_`o-<7)M"6B__Y]+8TW&074oJ3&?$9;rRZ2FJled> %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#<IRDGl_kuF^]JLGm,ilkK7 %-64i_-RjRXEM^qW9)<d46FiNbn&Jt"_7SJrb(\C:cj)?rCY"MYI:+1fm"I+*mq?*PMOilK_SUFQg#f4,;os8tZ=/+5f*r7=6&_if %7HQF\'2*dYXf\KK1T/+.@pjFtY!QV#SXBr*EV%.KI!c]Y7W4Q?rV:%!q-D)$$6OgFlVQ?2K5JbjA;]1[Y`0[4!u5bi#Y-C8gKV)5 %*O)t/6Rc/5d>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$Cd<F(tTK`(=mt0o]WJgphsGk %XpCB:H_;nq;tS_[A('C>hU(#2F)bB"FPp<*L\@d<NV@LO>na[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?E<oW$A]H=K%7ljtV.&+emoT<RCp&#WB!%UOdf_3nD@- %H1>e/*Wn&_*E?>niFPT`0?nQbXNgjMS&SBYIs4Bd_@Ykd+a!3)(T7G6I<%eP1^G"iZY*ZN=E*K8l#;m=ZOSH"8p@#MZ_^Lk4L\cP %a/&O<oA4R/0C7R2H]JY&0?4dddWt?LFIl3l_@ibuQ`S;!ftkf-T`P!r4uDc.#Bj;KeXq?65Q;\sM`AKY`_\FGfK7$p.M.9!>;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(](5U3P<gltDWJ4H3Go62T0M-<ceBk8+ %!R7CT#-,n+*5k<eqlC.4FOO'#ghWM[[&JBg(g6@2Z@T.*VYT]G!F5ls#_=[0oG'3ZQs(>a.#Yq;VRGfuQQY(s^?@inO&#tt0ih/) %'rdM>H@lCZW)Ba-$T;o[TO<l?]/ef/>%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#AS5UG<c"FOj//4@-*U]\$8EIZM5Q)Q@;<6*ef(NR[]&JlqP@O/+JnN>IBo9qk %,)c=cU%E&a;u!`;q-jm8_T5ju_:V%IqpF[I#%41KcECFX]elF,$Z7A&(r:O'&YMm9BC'A<SiO/>5ED;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(<M63RI>@=6,a>9USn$dl20_+&4<n5BS; %PQVi@LTX@)=A55D`Ecsq[;&&GN[kdSY3j*B*H+m9k<QE/46L;m2GcmCC@Y\S$tJP7Jee=&YL7WPeP*m&kL??=IZ2[BE7i_t+_'PH %5X"Q)<BFLRi6,`Vrc8T0Qm;2$>+eJ#QCE`i*6Y&"7O-$FT-7IuD0Ce%`gD1rIt-9,`kn\SqF"^F.j/Jh+$e(Zb2+8<r<)(:KFMc# %If$G,mi)+t`T>)L#G3'Z*4HN5=E,CZKW&Jeel-H)qQVKPi>_#[d(p5eIX4.9HD3nk[E&GqLR<X?L`t!Q;M]1-_HnkRG;`9,UULN` %]Y/b7-6sZAKmES$:4#bO9GBj4oo-22:ajOj6aP?<R/f?2R$]!"P0Fb\Vf48sq<@hpTSc-T5FS\4G=jSRd(Vhki-uF1,qI]@GW!#n %k;sSQECCLd#9a(G-*g]]Fau:2/mL@_h"H,N(u@e>FRdI""E\JgA*WaDC$`Gqr1+;<rh[6C60k!]H/'Ie4ZHaEe2"W?)p$:kad.to %4b,,i1a/OD_!B(E3N*'%oMFke<>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@ %-i<aSeXu6u\?Y#KX*/H&4"OVfM\I*A"7j'u-9;?Z/IK&X2Gk$GL7Dn.5/$A^(#l1<W6%\4Tl/R\/MTM.@[ZVd6%na[G');![sP:= %#(*&U-29*,i$q+:qm6A+f4?2=Y`<ATLK96\1KYS:+N4snhBf$`TFh$$kVMp7@qucRqABu4p.+BI\0!lN]?q-0@*bp1BnNkHc#eB# %GY'cH8Su?#5:Wob[*6#k2M/Q#"AK5D6Pss-T#uahS'O7-#E=ap97=IlEBB.!2+Ys5X&"3@q=UfhJgK2ac`a74R0/o8!cUSYTSYVr %=LuDO'MjB@'+13-/Y4PX81Z7^pS2%WqW5?AM5mUf`J+!a>5UiEOQ.JSK[@8b@cLM3J.#[Lj7N;NAuW;V\!TE49?k_FpB&_YEId>Q %/u\=Id*?Ad!#7FY&rP/0%7cJ9&`0i`QZ.[U8Y8(`Xe$%b7Z1nTGcN<b15.E#Sh^jH7Z.)YZ;kJuZfa`Vg0FcYeol_=[VT(#PKomZ %,'*2`YsTU#4@4U4-_4^[)k%=_`"<+bhq:8cBU:,MD]mVlkH]"5F/CVuCV9!Nr<":G#lb^Do"7?&6KVSg8<>:['r!L<\@ZA:+Q)^) %Wh^bt1L\%;I@U&_K9eIm2&YgscCmNYPX0(BU7l;BY\tcW?2[ar$]I5V]--$f5p1WP.k]2-"ss-""'!,AVLC`)+SOKE5DATSWJ<Jo %gmi8n0H.43hU/Ku"8J/ZMr>4\.pSY8XK6)N2=mtS%i&C0Vn%L@d!d<2`35_ZU#G'>O9**P:(EF]/T:sT3-1s]Y_huIGl`?Fb5/J' %['Bg)84-<K`Q0abZ2ID,$\aNIQo$PMSq6;%c8$rCNN26$`>Lfp]H!R#B:#3SOr9Wt#W%Yn#d?%P5j7eta9h$bM(rj'$J;#&?Ici% %X.O]W;?[84p@6C$'d2jf`lT5fK\8K$@51MIm<`$jp2SB-&i5bLi:WWgS<AT]RO;/_?Psae*:VH`?6)K!5X"T;mhB\X<anCdIP0@< %_M$Jas!$_sHq+8LgCbHXRR\qABb0/4:hXuM+%"$:WI_^eZ/p/=]1!g`o-^&N9fo$J'(#$0/^b+6TOE"'_;E]Dkjkc%(9i)XXAJs_ %#iA.=_>i#9mi$9t.3mV'*-B+VdSNDsdIK`9k94s$-kg3Hpr<P]HAGW&3*b86/6j"B!:[6N$Z/'/Zc`R:h&^QfiFQss$n^B":Z/^- %+be4-$a+jnoD0<6&mVp_L%@OX2>Y.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?<L9agK_]L\Poi*4"0 %)KBUhbP(=)^i%ZD:$H+LIO+4,_We]5BAhH0]c#38$f&W-2W[s0\3'/aGsQORYBX$&Sk`M/G7#P&-g?&6?;!++4lnJeL14^&KM&=M %jTq7c@qaJSAf"-o)0[(WFgC<[SK.5j_^VR)b1SW^g2JNJ1bUM=M9R%[X1J?r16ja^TM8Z^WtBOhIm0_D9(mB>=I*4HRqdK<QR*"N %Z,/j>hmJ01!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<HEMCGn9B2@_H2K8JmQ0E.p4a"8?iYD`n@DG)K"(B37&*V[eU)2H+41+8c,S8;n)?HZ=a9: %?l?9nFmuaJHDu/(4G6C(>$&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?cF<Ec1j*oggCIQEYYAX0ag6u6L?C)61ga(qo1a/Z#Z!k7[-\eBb;,K.!LYsd2g@Kui*U6aSXET-<(KIZ;'8JE@I_g5KC %Z^Wt+^:a"8c%HKamD@Ia/m-'E7c[m(pY:&S(o*uOEX>uq-Q#qf%q/L"l'1uXim$5kmY46rfU1X'$6ef<1Be;/d@i[T2C=*j;8Jc1 %"K4_(?B-<JTBQhMUC.qP2ru/\(<AR=V"AGk]-HV<L2^f-4B3kaa$+uHb!Z>$%n)[3P.LQo+q'?L'nc]jIY/FjnfphEgqQ`'li]0h %c<r&0#.T'e"d"MbqW"K8eudKF$!d4FG>A[$<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$<s*j,^rX<G]T?A2X" %eW9SQ"sihV(QLm*AgB^TO?9[>4cOXN5Qf<%a.[C$S%(O+VOFS'/WaFF(g+a/guqiULetH<oL$&XQIRjBY%5N9h@;-X;TPruG]t<F %:YIIj_rMPK!I&N`4WYmNVOi3%(*s38lg4r(&mi0%/-p>uT_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#\<YK)/imC.$StK_RAcm2@4A=cTj;$`F%B4cc %1aYeE.TffY_QHW&m^RCi6EL;2C^[igD1p9(h1\jrTKWo!0gZ:gTXcEpIa:BF`+],spNX\j25s__-;k:S`>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)-&UT<JD_"&W0n7.3@?Z]h"rYYJ?6LgfRsV(94ZMb;>06.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><LV%GOqPHf(8F;0l>_?21kYVL %p2cD99k3J7i3ID[nQR:)H+-ILlTU_pfcru3kgqal59gEfWW0R#5m%MS:KAc8'@n8iAf$S>O<V71E2hq)C^/8Wb"<-,](_2[(J',X %-;u'g%pK7bo*WNm'.LkEP$mnr^'Z\'FCPZt$N<09)]\'e6JBrI;FDpC04l3%.5F@-c&DjI'9gqK0G3&m9\M+V>@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<dtrcDj6L %SPuQ*$feUfArUd\jLcB[8d.84^#WFmKGG7)R_@79c=RJhq53YB"2X@e'9@GL>'J)8=tFk-.6suj.Lm%P'LufJbRr/$&DFI$REEl\ %I0f6lGB[crmq5WaII6#hA\ru6c+0".bg[RKqjJHn>>,<eqbig5ZGG/N(FZejHtsfm/9"60GP/f`C8fEf,6Q+ja+:0n\d67aM$sd% %_8nuL9JQ"DTN?Io<'<M!)'&^^qCn]M3GEpAL%'IUY+1,1[&=Konm/<tI9'FE`9q-S`U=U:,4>LGX>(;$O5!1[YnLiRlgii!=?A`7 %A[.Rr*OUDMN<S47+$GNW%*Pu4b6B?m)oN-Zg_A;"+WNU?$D;#iL'[UjZ,_%>,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%Ff<i+?N+$n!Cl1L!smcrPZAA/Hq`D'#=%M2U_X_,p<L<+iKtR5!8] %9dlO@M-%S82Slu_6=rA?9F&GZ[-/1h+,B;-*AZk+1A%@l#:AfMn""Qm%1q<]75WBpQoL[fdDB*(4p.4!_lEEn9Yt^"-?5#5'9Y== %HZmTD#!um']LI(@k\!CcR?``PRgVLrCX7$R']j/nX7CdtO2]ZT%uDL^Yc[CkP:c65d-)g[bs7PqB]'jC=VQ3R*=6/>ehU986<$De %=h6",a+7m!_^ApPEcOhJCVRPXSq>TXW1dttpH]O8X5>/f:u<)<bFfRb$I3$ll]s.*m#LgtGL^hg)#m*u+&*1`fOI,Ge7=ciH-U[a %"#`CSM^/aGgieO/^p7Wf!$$ThE=cXE&>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#<H?hp$f0VYii"qli3"d9P;Z'`GOc0O`%+q8^MU,^6Zk'_mU3/h\Y %3mOm@L$q]hN#)R5s.][prA%8)n@S0$2iS6!^%Z:$==#22kfT+45,!GUr^Af9Oc]G]EslH7KE,r;^Hcj5O_o\)j>GS,SnK+?*ORs) %L3hh5#,*M^eKL3OVpYl/P0,s/A%%T[2!/dV31Q-kR.rHUkZ5ohreh4Tl+V]-FX%$:FFD5f=cmA;%03AWe@\=1m,<JRW<#l&nae#l %3LE'np+![^>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`$<tsWk>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</r3_2#iB,2_oX@XG[4g<]r7,Ql)J'ZnZ'H^P<[*cQ;?_Tqj%;UET %0r0eOp,rkFU-.B5+\5bSGh*`'3cm/)Q3pCP2A6.&F\Tdf]c)r,_FobGaR,ANN(ss!@&NX)o[4sKCuE)Ag_59l?@5"3pGLnPkU"c% %;UA&`0VZGnkp<W9>=>8A]`pKo$#g;-'+[M<H$EtHMdjOO4*;()BJ"\*<@9.a:'dF[hG&m%`ZZV?a((ljs1,<L,2Lg!i)gKZZ$fVa %E3#IC^_&hC!UEhTf<kl[KmCU-!bE0XVb,1^16:*O6mQlFp*OTiNoAK)=_Pc"nEP*1S@FU_Pkg?#B^IY'#*54H,%\H<BfW[(-FjD< %2%p!X0B*sDpKBJ2(WWt"^ab&6n[+uY2"K5`D&O`=`L'5+6ef[@ck.,(5.,Lip3OK$;loiCZ`k;ric/'?U;h.Z$Pu^fBM&RRTcbTJ %8Jn"i^i4VkE-tAp(Za?[qL!AT_K2t->`<Mic1DQZAt-2V;9Y<I1TaeK4R+kcAU22B_7:^h,(pOloOl!)gOf#\L+Am_;0TsN3!^o2 %k?O>sD":6G*:=%OQt#bL#R#GqQAN&R3U^)DNHH.EB@&B\etr,:c'j-!!IMtn#GqB(YXjoL7iCc+>gV>^"K:(GB0E<fmPkm.4c5qZ %FcKpJHkiYXKt%5ePuO'g"rD_#ipCPe3o+tW,,C?*,+UY],a$P@TLar<VQk$FZin]=`>Y'Za-Anb"&h_7F^NF'j9b*O5%Cs[lJS<V %:;c+-)(S':V]?IuM?;<O9Lm@f`G_O[S)3E6NRA+P'Zr[!U?,/e)VM$!N/:uOL=u;*Bp)d50#6$M7WqoEZbf9a(joh:J;YYE-fSB" %X;@?FQSCM*?A&JJs)DgpR)>ik#4^`JC*0oU3u(MRZF-dC6]<OT"T&*0=Y?VjNWn@t(Hk"uem!i&LlRLX;1tKX7\a#bc68b)*Pket %T=]sV1[^4Idh5aPj3bZ\hCb]aY6c^'6StBnf)'TiKlDpuPPLgpZ]H,_%BQRl=X:>H2NqV)KWn'#(\GP26RL,tZ6S&3<h;EV$2_Ci %.TlojZE_gue52s\^/`elL5g"dpK`#JnQrKVU$Y91YV!haMs.!S<"^?m<Fof;4Ll1i[i`;ce.S2$Ag8uuT@jaObLVB,6c59"'_A1' %hRWtK5(#W1U\9sh?`hKW8BSr.Xd!oMnuh4T8Q1jI_ELMD!M/<<a.AuZPTibfmBYqk&iG)Qi^HJu/drmqS_:K3G1oZ',%Jqu<FJG8 %KA(C3H@YH:rk*,p.6MkuKLF[nF0RV_>%2L6dm.;*jdP0@_8%PuINbkeDt@gUVBa'j1It<CV`-o>/@!0rJfXicQIG,AdQ,l6g+=dU %Gp7SeK1@/<Gi@=i$2H41?c@4n!rh,<rjR!E)PV&d.oKe<3]ZLFoH5_m,qQ$Z[O`Jim$S(JqlDggVZ]i>E7+Ks+@'d^dP9^G!80UQ %$A[T[<pm17N/L/3LU[D-:788tWTPGC@;IYrbaGBcX=d>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)r<Ub6pi;E3>sG^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<<e]%Y0T(RX,dH-.oI<W(J;5hhp:p'YTnj?eoeuI60*,0-brP=tM0K;7tMBb$Yno\e#&=*g>s5Q\&Q.$IQXJfV9Kbs)N`\^7= %)VGh4arbg(QYMA]6AQ5UX+s,"95ge$)/(IT+JOE4KfP:T]I"paAt^j#<WZC;I%'/m*?'-SSh5TI%dHSr.2t'K`QH@Q'b/4i)JB96 %pjC)FbQuI[K>4K\=)FmV=Q[Tkgi0lk6]%-^(gn4JQfNt:L[Zuk-b*?<JbuEAFXb]Kna0qYkePk]`:fOC<L:HKm-!J,"PLIeE(CZM %YW\ni'AVPX.J6.77kmmf]tBp+=*DN]EUM;m"]+TU27e$#/aO8mkc.SnWcB^4$p"BM>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=<!(35(l#@,T?:(gn?d!YIJOcQW&U'j1S`)oT"P# %p8Sr@Vu.BDL[qXUbN>XmDDO:tHPJq=lt&LX*?SCo/c6'LbgJm.T7R=k7PW@9=E5RQ=BT0._^\)C@9LYCMm?,%^(T:1*']*%kqKPq %>\HR[6b/<OV24?D"EacPVE,.`!#oE_EQ=&QjYd>;\0E!M+=;';W%*#5=8&llSJPkq0e&%4MJXc<R!,834-M3uCV(i($^E5hVBhk" %$e`*a^1p-a\RrSu9^A!/gjVF)Igr+`(G[)@M.\bc;*3;N?1\=p%)5;t]?P'hQ15AAo;a>o+WaILWt834[EVC$UW8%?`"QG@2u.,f %eItqcPCT*@'=4`^('@JM4g:Xm\Cd9&\MLI^C?puRT>Z4R)"usD5?J\kiB1]qMq,dPXDE*8bSP1g%cMLYWqSjgZsTZt9"Gr6H7U4% %bd:'<J#ZYEYJP/4,H/ehU<73m&2)f`$CX\+0l+&eHZs+1eq.shEl!sL]F,Tm%4l[;fL<g98`&A;RCXQsE3XN\Zg=&dUl<Ss\;k^Y %,;/Z/EusJnLbXieMcm2M`bt&#cBoDQeZ\j:Aj*[flqlU#c"\AjZeMpHXU_^[V(9(EY48B4X14P^KQ#BTTgjbe_T5E]J;[tg27a3e %HoJ0@):I\K"AM4M6*IY[e`s/^'%:JXf>e)<ne)[gI@+bmPbU^t_u->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+J<Z;*DH$_rdqbuoAG-'F&OI9eUnoT5*\3<d[K1?L*? %@TJ=FoY=KW-]*M!<7X@H#k3LCbJZ)>oY1.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+CN<d'AV4]o!ooeB'IF_;pc;Z]E;="P3IFKMa?)eX[Pg!^@<(+d@qh'gjtU.D-dX1ERFabqA6/MHGENZq2:hj3l^%ZSK6EspOC %9fQ;FXU?mt%;9KU8+=%DenMNaA]Dfk!F,k%$nonG[P7nM:`#7)gD;7<kZ1%bmPYM)*MX1(b?=NOgL]@2@psmK%7>AC-s3hL[ZpUS %Vg"hAfNkAdb&,SF7<sd?>6hr0ZB$N^*2cXB52C+k_]iSCqq?1nT0d_e]'CRShn,BTnV"9n0o/Xo;=saMWo=Io0FZ1b0&nEs,p2)R %X=WOuW*n&l1>c5+k@H?^`gX#Od</GO@M#ihX-E9;Hd#%tUR4h_2u<_+\3cjZhqMi/kUfG&oI\-j=fa=Db95:,L)e"!Y5j?7"8\Ga %`o<bGK5FQV[s7I[4'%e)O'EY:?5+F'E0H-u^/^R:71#\Xi(+[9@TK!,l"1((_g\e2EI0NaNLU.b24no!h/")9RkY$jFt>E*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'n<?[SI`G6`Rb@o':B&`k:d(Ta/X*EbAmAR>T[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\<Eqc#eSJO[\3.hKR:o'/nZSM6]_r-1<OZ/bSL+jQTQ %EN2dTDE\26D31J@")8MZ$sFMD"86T[kESi?\-%\8qm2q`PMK`Dg8G4a_bFO3i([02]l%&/WWB+3N!.PFO.Sadb'OcCgCHaCP-a/Y %etMiRng;j=b9D;i`9uCfM62qIDd=2oCDSP!>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[)9e<F=\MFs.=GF_P!Em\suXi4FFT.?5Xg1,(YbE@ZiZRAlYGWct/FVVTk#0Y`OV %Si>8b%2_^jT$uC4V<Z%HDJq'1pd(N2Z&50#M#aEuAK@jE+]Lm$^Hj>*!&4VZr9lEs7mZBuEH_SbD<h8gD!@*DBWcHrF,c3d`%;m6 %0fH7</R,L.d%G+-;%#%1Fq+JemotnP=`;-\@/5sohrcNQelF(93/&RQB#[d]__W6JP>T1i,NE%aS6R^9)RiZ&/Gf$J]LH?eg*V5# %k-3<j3;WJB/@^otCo,TSnG%XL0Z8rfp'.mVOm);C(Q$X/"\WYP_-,uT)RNp&r#VUEs.*)%IaLD(-GsF22+YEpDrI-PT^,U/?\fM8 %';AigU5a%/-P@($Q(^AWf0FrAa-,!GWle#rS-]R_n>i`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@<U$f"\p0bmNI3_99VrBXEFY"P!nVS+0]XGP*%_YY>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-<U/ecHrUp2+X>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^<O"mZ+*^@n\m2Gs"i)lDA"lUSoN.?/JP" %:1]aohKG`[24C)*'CX'phF52623:pNC8`bA.:fHSlZcME@r53ge&d!#UBZY$O1RT^QSrKk7U]kRHA=taU1e$:pt&%<-ap)2M,@K: %MJ$(+A"9p<-KpS?/LlR`^^>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@$rnAVaD<H?\VaeV-hdoNe:a>prDY^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?<?Yr"f.9J\FtI4mShS>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'<V;L-)5;/U=0)T6NiGnh1booO_Zn=_JdpVGe$)arhQL;.j6T9$\)\\G0$#ALWOX$8%9WD244[2-+fg&;_EhZk*oF\-;.8 %-V\$M)#=M6*$a<3i)t"ZfGA(m!uX%D*nor>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.Xup<Jqah\(;rDk!")Bdf<nqM4C:lf93:80AM"[t[A4nGQ1uT] %hAV][9T^4*V$t[+?$:t'0SF"MR;JEILT0"P#7tZBNp5olb%JcK7!!qs_-R;#_Mm[e)`WQgd?+>Y@gM0_C&e:BY+-,W5D07g:neiR %k"5?JG)Lnn(^AZa%,<^ISr2iFBs;hFZh`t0Z\m@g<S>G>>=oj8q'lYGe^^;Q(hAo8Z$*N`=Xh@D6<5n?`O_n'p?jkK"3bd*(Stij %iYi\,s/9)]F$nWMf,0n<MFV]0RM;a(ETblR]WE?72Z):O1WpY@J"V@W\3sK2bXS;3;Co@0l63\$(R:`f!#r,R;B@SK(3FQ.e@U%n %@@o?\mY%;S01r1nfcn;(es3:G&0/L1]BYEfpgb8P=!1Nplac\E:LS<TogpIHCk">!"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=<1Y<bbE[WdQ(nj-+6GcFe.`#>P)@%-2N\!1M%T,Vj#fK0!2@[$Re:<>MIcdB26/1Rr*K.>Zq#cnYQqI;B9Sd-"L!& %?kmGbl+g\a4hCrj-(*=(?o1!B_,!$m,SbR28$NQ4i.q#/![ABOCeqp<h]E*sJ1\cN+eOn!"9Z!iS8ljq%rN3Pg/)2MPjX?]`:+kC %6oMTFkQM$S9W/6ZWO?R$!2Y27m'b%50jdU^;u%>9mZoc,W:%hGEcIglRW+oCMMD;knlH.JY$-.k=bY6"EO`JV$?+BIDNeWC5/;Va %kNQ7n#S&"DPNISWBQ[:)hr0+]n^E7Z9S_T_<l\aK6nbm2m]ZcS_@E*?fVrS*Z$%/S5aseF<lF;I/6q+^]s:q,JkdM(D6s[hD5s!n %"dTDt\cuc&$u@]%f7n$0*2efo`51nNfh_o7`N&?l+<tVQeuW)ea:5a]#jOCR_H,bC6q09VXN$o7P<>Cjo0Qd;e=D"b+&/"^&,b'W %-5uL69g&2bIWeO]I<49#=LlV>>D^-RB$hKa1tgBt\>=j"I"]dV-HpbU)TH28FDaKOEoW$lX^4gl*GU=]iJ_WaZeNSIR*#nP<I?oe %/DBK!M,k<8ipU"NdBG&ZHjW/iSnM+PjR"oOFrFR1X(K6T]/#shS7JR??7UWdNLS^]gff/<ZBCYt3%]HVTRn(A;>R`ZKdS$f>UqYq %YT?ZN%?ED.F[Zu,Q!<><Y"]?FPs9i`aP`>?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]aW<U@/MrNN!- %XB4F%S/e"s&BVmqZ+.ns'p2Rii(:"%e'"i8Z*mTC9I*fr7`JPEfX%BFOe&'k%oML=lZ\larrQW>Qll@/)Rt.0'$V2MbG:P":_f+! %)aYbGqF,_iA$Bn-SfE_8am#g[C!XYmD?Z=o*k;kmC_nYIqE)*(WN[=s"I<._r%;#<C007tCKGhl<A29<0QPhjq([O+-X@*KPI^`; %FR@MS\6s&*^I$6L+*"nh:8T:JL851P41FY@(Aq%W5i1km7I%EOg/t+&:^G1uerK1.72s_8pnMQ=,_7<2@Uo'pJ+i)D)o:8E>ZWcP %\<lL>:/6@KYT//Da)EX>SfF:KdKO9#Oj-9nE>He"'s_/1<SGCEph[OiVJUAN+Og3.ia_F%:#8^5E:`a$_PLV*\Z^hQE$sSiNVfc* %#p\*3&2oGlquhK9U8.(i.&Yrp*c-SC4m<M0*684BpauEd43%__ZWWH?NN5/CZI[H#&O^sW4';bs2fCSb7&gZ$#c#mL?+H"HWc[M6 %?0,"2&.?La%&6Pm+F7,+oc5$VJej%XeeJMN#$[ATa#!lKriqJ=79>d<S#?Y_33l?DoI^FR5"3m[/rFh)W(Ki3^S=(oEC/?bW1C2r %#5j45#0a^#R/TEULDLe:JpK&-+B]=lcH)DS",5I4`lhU%M`sujXej-/aUg@LWR'@E98T+7PTq3aJh@Je]>=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@e<i*3M<at%W %gQi;U6M,qde<rD76@U6D\1$c30GTi6<iN42.2H]fI(In3d-VZO&J3:`Gc+ZnhZTpJ9_DOfC@oljSu]gP4d@pZ]tCA<>XG^%](#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,>"WZ<Nq5T2/`mUo"1&;[XYH5P$TV$Fae"jL4`,PXUXAhUg %Wi'Bk8ao\YWA(dZIE^KKidl'W]>U6%X9MFZ]]Y<gWQ0Sa$X2edeRRb8,r+XEg[rM-kM%>:$/12h:K\jtg=;."A6)ec`47Au_*B_M %D`,utGjZDqC`/D**r<iSh*Qlkq.co\D,6&iT[5._g]eZUQdE,O(Ph%(N)6oLg!GEVHsC<1Heet>^'.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<E$E %gZu<DO\;MrFtd^Hdt:ejXi=mlbL6]DlI<kRRkDC%(E\(]^f'1<.GagT"-,mZD*h$CB%K^-UK06iDCRIRO$G,#\1:8^*0:nKO<VU] %SMY+P9<9N2W<<1RSn/dt=3k"6@[u^a:JQAX7$;rA9b@]Ph6[1EN;`'JHfu^O+L4(H_feRp8#-]qpraG**6p+=#XaRgkZ@A?ab6-- %I5H6MbagWGkeL[8c@K4]OT9+_SCTt9]M7=j^:9"Nc#?'V_Cp;+PGTL?0RoK!oQGnYT[+rKAG]Q(eQ#qo[4`Df.M)2dBFfE@'kgXd %7c*+XlDSj0">)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=;<d_l[rE&_!9T-5Z_$3N1D/"F*eaj;[c=4Ihd/84VE*H\>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<tnNkT6S2l9rZ %P>>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%<M#M?A/IBf7Q4>Z2#L"c0RVpEah6071HBrO#@;k?,Z`!;'XBNde+ %S5o6[ZT;5#4_/Q+/@'a7!FVP3HP2Z361V?tniOi?<k>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-<TP0'386TrTK7odkME1FDhD=aSR7@c`8cVGbBAJT`,AE&K:7pnY!7J&thnsr,lK$ktBa5/_r8]W%Pabe5s %.FC2<[=cC%pY9&%a4s4:N7(]@a3i]#/,$8mTr-(_<ELT]^teEDqlH_;Q&.G8"9L_@[j'kQI!Q(!C3L]$hmngVi`0/@B5'!Z_7Pa/ %;)3'!iSs2pFd\`sL0b[Z:li.`br#>RO_ci*<IKdZ@HLH_@g\;N)^'7^YYr]A*%>8,6PpEK(]kJ3S-r^:s"Y9MR1h;)T7oR6`-5H5 %(Y^dM-jRD8,&C"Nd%PFNL<s4-Z)$X.2Q8Opn>4HfNIS"H-:H\A(!^Ik4A3em9?Dj`:ot^R1nbcQ]"$WEOej4uJhi/ap#C41<5K@a %4#f-Yp"pFPUk<Dao&+eQT[TE%*\nn@'jF%p+6G[AC`oZmicMA/h/TjWZNXb,\Z[g*[:Coc@%6-u]Q&G;2Hl]P,jEhG1:1Sk's;n5 %EO7BNJW94s\cX"jF^H;f>[@_]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`R<ANcCM&"2erU``i49!_>tI;.MITa.mCqT'uXl^("l2g;-EL2KMittDI!F4R8RMR--meQKrb=*6t=o+ %2&4a9)Y7cQL^a@^7j2[uq)f7=>of5^+-L/h2<eLC>,GH(/r"mD_6LSE#jFr1=^@H\:r?a[g/SEh!4fA9GS>u91*LkLWrh)8q,mr- %Q%NJ$mH2AK;'XT.<Ng_b"/P"YP-W9B.qF`ubD1\eDd9!$B0Dr,b6eI%lcA(U.k:uh?%kU]O*$X!('%rXTkTM;0JGI:$cHouC?F)- %(CL>u-<.BF9ghn'4H%_*\i!KoYFH8W<WU;1H-S73lK,UL)\n*,dJKa]!8[&<*dm-jg$.(aPKU*`(bnU4ZoJjV^3LCj=dJ(m>/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"j<I^>3mCB]*q$&79rDI?6?U87N#t-ZV<aaQRJR&$9B)h]XG2o]Z`PsZdr[r^@T=X]$h;uJ\UX0eRbNc%!ns(CB %L9i(mk3#3ord2.*4YMQd03Kc*,fN,H,h<.S<n$2Nki_'+^L4;;ar8W36qhJQA0eKF^]3Z4.=uJ&6Ii>[]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<c]#\.]oBDa9E1VCs55n!H%<nRfWh1#@) %=XEbYF,5rL]9Cb*c28-:20Kd"1_WrdS]$oC?a<lYlX7()7UT:OJON0*:h4a]UT0"pm]L)<F)c<<_L<B'@=ruNT<]LeS.tMj&q9?] %(>)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`<Mgkg+#Y^?@'I^(2_e"*`o8W@nKY@qr.nF0fmHb*$Jc]+IG5;g@0affia42f!C^aEj%,30<V;0H"^l##Cl %l?%,X^o8+JgJF:ZilI"eS6UE6;J4XCfW50(mOi>*R%Bd1T?W%SFK%C7?TNC/"fYSJKK'C=30Q_cGR.H*.uAQJq/tTFO&+f[IRL34 %0*X,]QuLHQTH1O*IuNQdFT8O.jMeM*'<EG>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].1nPWbhI<Q%4b_So51417#k[=s#Lkb6f^b5Bfda7L+mLoDR:uQ,O]_FPF2,b<K\o %+.BhHAfhL&+q.2YQm"iNkI%L.OWL1miWC=CjZZ,Fk/'=7C7P:L4lGQ8r,jF+B#MD^/70mS8Ilt-dT]IUo<6'DZD4SkG;Kj6:`lj$ %Gm0^O*'O%HlKDVI/^mU4L,M(G%C<s9(uch=Z0)e3n6CVVD1'_\hNgqt8?6)S!(tOlF`Kqa&qg6':Sa_=&?/Zf6i?TCN/i1ai]hI% %d]4Ge3*lF61:r:hB'+RkC3aUjKH]mt*XX3t5Xj;K[j]!\%N)@,0?sPr8L\8'`/;ElkQ<rL9UL)g'*%UQms`tTs2A@Er-/1q(Ou9_ %J,MU5J+qk#i?/']O+7/Ds3L`,pOE%g5Q:0`+oIgZr:g6hLC`CZpt[%knGDs0r.gncrQCh]c[Yoghr"CQTDnc`Du)KoSG^-OpR`LV %q&^].pqIlEs7g[4q1&8:J,_a]ci7D!r`fDqnd>pnhdunLhkX\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]1A3bMj<PZu80$U4\ui$`_e%@d3SV_h/[_Tp %72!9uGF(#`-D,q$,6\bKOiu.*lATADkrY/$C"-(:1Jf?7LG70i+lg^(HFcIu$Qo(`#EG9lWRAFX?G/]gOK`1RS'<p8?'4PO<C_'^ %OW`7;*kI$uXF]qtYd#\VDOCr]lG;`=Vj?#D#*Z`T:&GI:DO-u7W/Y6&&Xf!YioDn8Aqi<9h-+*(@b!`b5Zh-,^5[0IXlY*<>0TL^ %2KfI%A?Q1bi#<2D.aWJZ<U\fg#$ISad#E`H=T?j[)o,[L<I;k\r@s0lZ?3_UO>&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%<BRKdYb %E>=3JRE9kH?*n8k4]WMYVVm_`(tp7-&ISu;XHs<N^/)/X9r+IaAk40kSWh!HNk8\n5Z\P>_:pcH:`)*S`!k2[(s"%-`^S6O8VXq[ %EbHgnJ'#1K])7)u1s>ZnPDBfRN`rr!?B.-]%58T2^10j3<?W:(Pse,c264gKLbbJokU^bPU"D9>Re\YUO*iXZ-pZf4+e-!XUr9,_ %GGqd_<Se=aPXFTanDq]KF<ISu*q7paE'8r";g.T&[or)NT!aB198eV]6T%-_8r#A]lbL-T!?7Ri]*6\/?G_X;=p$d[E9X&)?M232 %;(d7t_e2h^X>A$>^'q1tqY8MMS-LTJ@(qrP!`*neRmg3]:HWo\hWf<T7n$tj46h`.Kd``05j-%K#b7=2-lR!>?qn1)M0MJSZaPLE %:hi]bLguqZ8/Z$tNk,C96Y/lng0$t@SWr51``bI?-b5u+@5PHf5MTi9e6!WY3Mek&N4E:B<ZNH^)Ma@$9SCEP0j&9X+r*I;U=rN5 %[kiap($BT6(BFigk1str=lgljF-BSG.``kU*gO@]`)<@P?egafRL3j%S@r,^_]Dj1-Bn&PDlaM5[pk"L3/"1Z8L&c(Uk>'^/$"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.<?J6B49s1sS&!'FGMJScl1e^&H7K(i(a.NVm:f.UQlR.(4lbH4DU&QLA(e56]a,d01]&QM,l?rH!4 %]<E)FVa&WcLNj'K@Fba*,j2ieU[l&OQmO9cZq,Z[;RLRX\[;l/3nT4u2\E9.Ff^q9>qMdX1CQ6%e\2&"4.sprWVPps21(8,]#O+B %+UB`Yl4M7(\*Brj#?-<Hk*DDtdtM)G5aF+_^H#8-!gQaEZ4A+Xht'hccCe'CW[['S&;giEKiYZ3e$7c&g(]1*1Rd4>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^"$<E6%!@Gif4=i6ASFVe_K(CMd06F7XfZ: %OHeb'p_=IH<#s><h9iBFl'$#;2b:=16n7VtK.+=\D!g37a2E*+OeCi>#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<N]1G$m^&^.ErbBR:e %=pg.rD=N5E.$?1SPg]J;NZ*"!%Um[iXUH>'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#^#;<S-KUhe5ZT %[$\"9]Y/4Dm=7nM2)R)g\5/D=rnaKh;N];!PD'WCEe8\LQ:tS<DOfbHIs_""2aK[+JD2,'@4_%IF\+IGLb;-1Nrl-A7`([kP.DIi %O=6Z6RTa8>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[<iH1>/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%Q<V?fWkFnn5<RhM:b3F<0BE.\q`>r7V\ZSfW_/<+-q\mFWF<??C]m:kP#, %;u*,f<_">1@o<'>/Y@pJ4Dr1-+*ZbE5ttR.WRB'KU.;4PoFDlXXR\QV.Zps(Zjg9(MeU6ZE@5K,iSHJsc";^1!HXp:2=];OZ[6?A %e%t<cXYsooY0JhC,]A8>jHVh`f](K[(1)',N*?EnIit[Wc3oCCd6UN_Dc8r,$V=l,4Wi[N)@<<j6]VQb^")I$FAh$4Ydd_LYcNVS %3Yhm71miKfA>1@g]E[RZKWH/=qqCn\_O7g\4l.)?73/OJ\r"XX7JFNQ)uVLMSX9th[42L"KaHXI/+!RkO+;$[b=Z;qK-Km6is$<C %P4*+R.J!YUApUK209^LGF<#S&2emGn&[s:)@EubcQM[4^oW6D\,#(9h=sb5Zs2/0Snm+Pjd"fG0EXUTr)k$<e2i^W@XW\G$GIF:7 %h#8TYVpe,`=#"q&paJs:RC%aH)mk30X/_Eg?m$B7EQJoI&>O]jMNgNCN8U\Gg/J/(j(A/A&(m6pQ81]/0(D6EMpqme1?2%Ld)WF/ %0noK9e^^G_>E0Uhgc9SU_6O8j\MS9O<_78OFdOmFW<PeOq2-6;9mG0R*O`S["_YA.bRm4gYX]hp5X8Lc<`mehFMttk7DAnd8X<]i %[m3k'_"jZ2KnDticABPa,HVFNVL3Q;DTG^^bFlbJM3*h`,Gl\0D7IWFI<1`rl&eIt6qu$*g@QP)@Hm_ur,G/'\YZ5[?CSa[d8WBO %R,O.Gdk:OEDYP+&HL4`FK!`aJV*!O:Y\WmR'9Ra:JW).s),0`I,hWs(@F50r"h5%pl?(=+(:a%:ElgHLEBp>WnU-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]<BNh$q8rpc#ZfR8Z1B!#!E":hqONbWPUe7Ht:UN>9sHmNC%LoSuuLIC^Eq)1Q6n,koG %5g?AXEn6aq.C,U-N\g^</D?'a?C9.Un+Ik_`kX'A2Usben!DSX6)bt/gf0S^A'$0(,`VJLbhlM?dV7c=M`p5YY04b&_pL4uSLgat %EMFN]r,_D::@DY,0n[D]nD#l3Wle!j[Hpj1H@1/VDk;_9^%Wi/2$_Jn2A!LC5p18\)Im3$)/$=7)0D<gN*f'<eY/-@c[([+KR#5d %g74)a#9$-s2%gcSo6A!r+Ae8nXW=_is4+m]f73b>5Q.($rpcn9rXXMRr?)'gci<B"oG?C9)`tNos75?&rP/?rO8o-5J,f+Ir6=-s %ooC2EIfHnWiD74tBE.Pcs5rk4J+r^[)6aR<VXusJ<FM&8L<aD)+G?f,W;"Q7Q0F*^,0rlEUF3+9BLdrD#[2<3TqE6\),\Ud&`.MM %dDjq,-_CRc<b2jd4Fb.b2*ufc4*W'Hd"uU+f>$5-/*rCb>/PrJ^@]Ed`"S*D7?TYZ<m5@(H3;VHZtC?\KEQJJB.tLB<[QUA`Hff= %RrAi`bNAkaiFO-baf7gH6.%H-&05Dl+Vd9K@g0t.)b/X#Vd9t[cDi9'l).J)a'lSG.9XT0j_6Ib_Ob<-":i@GZHapVSplkW#0Ntm %+NSnk'8<&jiQ#jnbHZ`bA]g]>P.\N>dUmrf`KCjtanE^^6A4)_rV'_1/%ZI.cpi\7GT:_0Li\eI*N%<E!\1YQGE4--mi*Y)5c$IS %2\:5!au-K;?;D7E[h.fLd=[Y9;V[186;48A_P4jF>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$<H<&K_:O>Cd'MEhs^V/"e.(4D%<@cY)YuGO$5q+)2gdApY@D&LY?:Eh'mn6 %/+FPr!-\^&Le\d/"$)(gSnhlDr'1@d1Jh2n,+tn<Bj@f%P76Oeol*%e2\'p;51,l7X#3(.`2o.:E$OPibUmpsO<5;XW8K#ZE^-9d %M[/kZA6&Q)LRHo8MiTWic4Hm`h*?UZPOKO:nEOHo[-4pW9Nu%W4ePqt$q@Ol#rd7W_`b:OD!*+QG4TUJ#4"9hrr]jo6=rJ"^LD%r %@'FU7UJZYEVoa6CN9^u=2?T2'q'7MPs2\hCVa.p7BOH+dpX-3[^P+m[&gre>e4@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<oL`9)W %+`\W>,#@Q6k=I$F-S6B7M@g^r;;42<$Rtp9*MgI#.=Hu7e'AQTmg]C`rp^L`NSc5Lr&Np;09/$<7Hf_VNK'UgmZBN=,=h'^^3u5, %X<jh:mj>a[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$<u`%t6P)#l@>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<BK=]F %G85/6q72b"\;B`j%"?*hHp#]mKXUh!LpPkPkePe1kJ^[D*1p=EdgnB)YT#\TQ6^,V>[70AiX&ds(Ud0t]Q6=t#1mR)rA9%7hd0p1 %JIl]V&8ne/ci?%b><!@fUui#;QKm"tq<Tdj_%:p0!uLC^n?'X=5K#-U/b*YI>)))'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.SE<hUOm^*n*b%/T[*!tXci_p,o]j'>d7<!_7>nX9]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,/<J5)XN4X?ic4jmD!e9(>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?<WPI[CcoX(or.3gc/&j8o&X9i%F/(Y;no(Td#d:_64[WM\j>* %fjQJBYYrFqNlFA)*PBWuX=&>0U#<;:=-YeHY.#+Z<(ip]3abS@^g,3UfRjpYkH`H]`roRaM]9I47,f^L3.o0GWYhS+dBX_((aKd4 %"hij7J4XLm[epE:c6bA_=QtYi]f#qWPbnYZ?@dUnC2Tr<bG<0(A2A><<dc\q6W1sfo+\]R).c>)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,_u<lq,co@=hnBe(`%'ADjEj/4Q&rM%gqHLS5L0>Hi7(nbV;9KE(ITOPc8=Qqs?*kK^: %`UE<pd4j6E[t?C,pT4nifO95\=.#iY0u56B"&flM%3l2pUqXD6O^i?>XtHl^C-D#*%8?$[-ls!f7MN:m""pI^]d'U!"-_g2P6Z8b %.hVX4W+quJ)I*P[/OqkNBQeSQ4uR&dMVu-db\l9YMpp;\>S((EI>mOMi`V?%a0SSUNk<mNn]Y>Zn)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^SiV<q(Yh/o5E4A347h(0n?'RjurYc@Q58)0;*J<m"rU^Kf@`C9Xi7VS?L(0WDAK>2u"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@FSP<!]70_*_1SG5WRhW"eL5&Viq:^D_Q;7YG\R %j*ifhBG4QuL7^n_LWs?r6mQ$YRKXiU)\%J7iNgh`7ll'=1q*@3jNF04M=&L,hs)@pW%&.+5(nV5]kN>j_cOhH49[QLWU4]J%jrUq %QaIfYOtQ+5K[=i=N[L6rq[hAX=R'AJSL2S0=,V'Y["s>o<NKuLAuC24`l/5Rg9Yd%cdj:g-sH:*_IW.%>-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<u&2B*]#n?4:OQ2),<oTn-P]%i=h5ecG+/,dcq5g@qc@+6)\'/"e8k#X(r7K5l;2i.e1Ia@S%[0'lF(Wnn).c?")+P9d@Wg %l=i:3S-97QMjkfTh+K;<]]$lr4*&Lf%(D>[2O\!0%="Ds`BERsl&<T$3%Ul#\Id=r2b3$+JDj$93ugS<Z#<(p`/"c;W>!H4G5#Bt %5/\\IDhABE1f3FlmTjfk'8G\e1@d^6)-`uX^c[?rA$deY_?QMS/VL7r8\(Kfll<bmNq/&[7)7P-f'eUc]pBg%p8Y^8)Y!,-a0l'r %UOfs0#/Rp)=)FJh*_p)Fc40)K#Q0!02EJ-C0J4)1nc(u3]<0em,["S`E;nh/#c*6@q#0d9^!VJN((u37%?1q<WR;5V5.%n8o_4ed %2MQLY29g=\Q)>0^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+;<W+68\-elbnrZL7gEa[]4%1YA:c7,V6&ie)nZ(\2E^$o)RZe6bQQ\ %$IDj!2(S?i!SgBu<dsfd=/MGN<g?_*[I/eJ;**+RcQXH6#hTJ6F;Sh/W?R(g]\#0^=Lau=@tN)gj)B*U_Hos=g9=TXq(W2$S:d/: %2mqj9S-1DgfS%9W,<Zu8L[T:Bj-k47olb]f;%ol3IMPsM:%jFf#jD_F/hW1(^e7u@J[,Puj-Zr4#t5(e2B=gn0COO*Me1E:.(6lj %6;5>-)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<qd9*;h%D+CYmn^r._Dp(&rhL\@@De%?!BB8c>[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/<o"k]h5Yu)1?1&-BTHapN`Hs\nqI_u;2s9/IVo&?P.QltTbt+Q_.j+-\2t3g32q2_ %'8gR>]: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.F<i0t(ad/n?KEaVk&Dle2PNOksJf''QdcP8PNjUMJSMug,ZQ&8ob7=F+,1DVhZDu&YHPr`F(Sma#1!,QE)>m5e1Pn=( %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<Z7[N-/gpI]Luf[!Fe=WfQ%9nP"o %qek.7rnaN2Zp1ut:7c4T^hWUS4hRU!Pttpf8&Uoj&:7<Ml@bQ;P__1+#Qi%1)`DXf0Ib4.nRKc7<m?H#cT9N:*AF[aiG5D^5dDPp %k#T.2_YRHjO,e<;6<b2iE9V0]*Fsd&gYWgo,ilkq`q_B9+>(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(,'(<f0g#V,Nln"3X4YIh_gD6Ddk+ %#JsCl-(2i">Q"qMi\d/5S-=T)+:5e9E^,G:n:t#aaY44TR)+3_lD,S*7VCcj\;iIb?p)1(CY/P!K4EOPR0D;r;?mRu<IkNlY_a4/ %B/^=7^`Yd<q!p<4"c/dobauN`q%i6?Si>cgCF.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;dnh<!=IXr;dFhE3 %G&FG_&.#j+)_a]rblm-pAU-t.^VLtfFY]O2Rf^@YJ<?.;AuT.]=uJ:FAMfala-M&lJ9@8WNuDQ.&DXAPX$qK\MRm7Jh03m$$,ibo %H0kh6J7:[j_@V3[@[N_u_lOOPJ75@oR#(O>Q&I>2^,U(K0)c1%YubFZ0."qcK=*P:/cN2h^]Ku0s)$[o`7U%`"og4VBJ4Y"c8EfT %7tdbD"@U,R<d*k(ZA@h0\--RH$\Oh`!BdD74#?'\56pc>p?/GV967Qg=Ht_`kRQcH`I;Q>.1WZ6P/hQg<9h)Q?p0OuiXe:F.F*q7 %P54m9;m]6_.*c=;![[5im7?T+>!Fo2fadQM9b,V<lcjU'#*aiH0q:P`UU2f3Z&TE*QCbf(@2(H%OpC<k"b;-5'`,[slPu_#04j$p %C5.V\hH!\pk[5dX'A6%]Zg&!1N4Om&8E7^(^d&@d3fjf_.1/Pkn6ep]i'D9L^u$.W403(Y)2CR(CgQC7R.EBL"`k,;ZpcfD23l[I %*/d"]m4Ha)dpM#U2IGT\WPWXW&S([c54!tH'TIs]%Q*`S1H;[MpeE7))681<J_$f8e/)(/j<Zu('Yfa)R#kdDiUNS-!PUha\W&Fs %=CN$Pj1mJ`,U-JO"KJ#i<2ag#-5,!6\pp4S\1dG-OWV8;OYCBpmBm$c^h/iLAoi#W5c`%l[9\!d?j5MeB'ee[$IsZ6]:[/63=VRk %+@D1UGoHhO":CUc#fZIJGlu>O_8YYt<HN:nP%>Sl5aiFjd^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-rhG<A0.O3,AQC3&&VU1M+c\H7_n.,$`[NUo[X3GkH,U\$mb6+:L6igH(m`7Sf+r,.)f3Lrnh5WpJiWIj<Of[9kbe:E=/ %q@t]tX+LK*K?Z-o,@R![UeF>uBDCD5msu4f9Jo(%lrs2fg!Ui/@2jCmnAM[7oV?=[1Kdi.'6D;i9:9CBE/kKJBi`=]3hj:mW*QjU %K:_>]!dE+GK)C>hJsH\.itEM(pNC(^c<fd];/3`,G3sQP;i]5L\8S2>P*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^4mM<l8)6hA-m7OBqBn1kTuQYh&*F/*l[)RV5Z??d5^+%/_9A8ND]bN>4k=9/2E@q**S,9C<IY)7(TA2:iaB\P@; %+%rbANQ,-5=nh2Cf),m\Yk!lWS*a[J/[g]a`a5S?<3<AeU/J4-,V4.J9aVA>Dj3Aak;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!A<D>2<[P=j&,A3Q\neU.i#`>Y_eg6F+u<lh4QOS:RffIh2s_X/+2).G/7djU3t"bHREJMBh]n]/FCPVk3(Dt"Z?c>4lt %$ECnsC7-d/G+@\/:ITGo@kY=rWsn36c[5o4mM$(2Rs*kKER5ZWLj6nKR$)$%FsX`JI7q;<C+l^36/78TZ6,.N1]A"*&b360+D\tT %p^pgeeO!rWETWm1Zu1aLl.iT-IoLKuT;,CE/EWJV$#\@)(1r5#FaHQ$=n]Y6"Lbmo0$>3LrB.$(N?]*b#mAkD,'aO4iAN<u"F+WN %,!f\e66ZDDU%K[&5oF'D]/"@LSk5#!GesI/*NS!lCk4FjS#Xa!]\@L)!c5Y^/Z@lJ*3\(`p'$fG(S#EtV_`9L_8+GbW`tFt`b8;Q %c7qRs!h8]k?E!'k@bX7[efs4,A`QC(]ehN`5oP*pO4"0W8'/!2be2\<BI2?!;NXWr.CpiZF`5hu`M`qcH`E:g;(R;:nZG;L\_#I# %X_b3+mfPe9'GEHk@Ks/>5]`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;d<kHQeUu`obu*e$A]@[b,T<=;e&6h*?q= %85X<'*+lEQ2JpV;'jTfmOJlM]bSU3k,AqVe@PQ8hWj\uQkWVO8HB>eWYGe8("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<M-M5qNTVAME;?P9n=4] %`VMm!OjX91O"$rD+d`.Qp2[0.8%ha@a@%U'q5Mef!uD_0FYS/F+O']0);FSBF[uIh9V:jD[2UIYY'Z:%/gs.2?FHN@$B,bRMiDO9 %JaO'QZ\N0[L;T)HG^AJ^%iG1R>!-\!&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<XkX++OokmI^j4Jb/hu9a7)' %PjHSR<UXH+C+g`C@(+@DEAH[KR4TL17c^!1@1DA02A'.KWHKV/5dD+>?^4/?BglK"8:f#gZ:0b,hsa:pIMeh`JU<rAJ&""g)_B*+ %B=sLY2Z*A)=@R/\?ml2sAPuCVH*KF%3aC5/pF[uMOg6n/"RFEe?M;mM+[;9ZS->gqG/hT9c`,P)^+A&=1"N-t9lHrd./*np6m?9e %7Z%dh3!$3$6r?f&$bV/WZCZ)@cr_gZm*uA]A\kMN`M<hm"kZfYic5=3;s6!$.,LV"T<%(P>eii8W9sD>+?.,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<Sf=KAjn[5chL %mQqXN%-!D1["I:d\2>$,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&AqKp<oG-C:r7c-<jN"tf[J,igd/\%Ig/Rq;1[<*c"fY5@:u[?.[-cm8Z9 %b!Y_I:o4mq\jJ_af3'>2Bk>.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-<Z,24>[=gg&H)N"9/X2Q-DuH&u?Pi&F`']p5+O_&;<]Y^2oYeM/bQ<.o?!WjKP'qB4cn %1.b5<e(=Q.1BKr;@)t,4LbIXG(]PZg%S;PQ7#VTI'5#)k&OKo_)N%21eQ14gg4Gl,noN$2gL<uE/T!.W#7uZQRi3l]:BtH=<4dbW %29.`feGDcI$j(jqaF_ts3f!\+121YWnltZl2Q^D@""B^PJ<,m-^UhsUD22)dV41E$[B*\WJO7b?RY2UDW(JYcP_9:OINJpIL4kd> %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<GMlrPM/I96_/bo!-VojEW5"a5(r1Qga0:(kfn9Y\T\[a\3oE-WAp#fJV#H^*+ptn:,O!0G_;6-N<&jni %B.e4L>"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$jGu<U/[X?J>hH"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%<HD_!;1Et`,=Y'=flFI.;G(J<S'1%bVdI01S;U(B0Q8OMgB690>&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<uERIO$jZrLM,XuVL"R"+?Wqq</T(Wr[4U+R_n=P@WbHm %3@DQ"38N[YcRCeT']88@^:*/@bd\>"dcKsjOc6]JJ2i-Mf2T^l!2V&g6rIeE.-uip^U$bV-G`8HCRM=tVoWj&&UC+YXVJZ%P>!c: %,2[8<"V+[*,?ZHn%\<m",Gg+^!Slo4iKgJi<4`flA^r2`9IID!gC9J;>6ui>WK/7',3mI4W.<+YbokD;^n*F\3YZ1^_+/$k"E%5n %+;eHqf#,2MNX(mMI;GEOoe`%k@!gQ4@kgb-^=ZmT5=<QK'\1[[j[[q,i?HCl$bfKQ?K7#0:esQsCSK%3HM"#;^/BiiTRDL5[qhfI %Q)#/'CBCY.e.nI^iS'PZ%/ni<8[/n41YJA#i9XIuDQqCHHk#GgWud<ic,4(pX-_R/!sZ?]e^1g<7)5b@F9isZnuW#gY:>\;@jB9o %nhaLqSZ#TT]OO"UTWO(MKOpPsnXKq0*@?<nCrgbsCLb=_M?sjQ'bqX>: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]<g'!%?MIia`4DCDk9"4=Un3,fsYP@"CqVs+6>`(](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(<kBkmFfuhQ&V@L+/8g".Dt=^p#KZX=H"E6rldsGk>`=!--^%LiEqV %X`A<@8V>DFZm@BCJapmCZ(6Tu3Vh7_'Pl:_ci?6TnZQs,1h&:#E=*,W@;S%X9/[,=crBK8CmE#iO$mt!I[<AD4>/.m'3Z`qP!/>> %oUht0bk-])n2bCd],>TL`-*UKP`[LL6ns"Z:)qt.Kb:cq2Ybn>,ASV;cTd84Q/spV"=BPk+sZJCJ[n<Oek].6R7bO\8;5]dYm!i( %>HEOLfqe$D;Sfef^DSCi@uqs=g5dF$nT@7rjUA:c-FJ]fcW"j*e(`eo;jiT3ip7iJ)1`ST$K6Vr.>fU!a*oOKapDtb!Q@"(<ZY]S %h+.nDPPP(_A=h<GOYuEb`bo7WX:#R`XN#k(d`Ge/?YKS5gR+@fZS`YH)<qJRfSW8[ku0kLaE,>sr2"rX$;F$+j"nsbE.ltQ@A\8n %J)F,EBpYu!P>;\/:$F"e8D`TGjMkc<Z*t6t`-q.c+9FU.lEED4q0L^s^=B2H@Kbr"3d'H-\-pr7&WQ8%p^>c++ce>:*/=C0R)sS] %4baBeO@slX?%s?DONi&ZIKh(FOA1%2Sq\fHOO&77^'6^QOAU@9hhFh\JCAl(s+X5+J5L]&+,<POOOJU95=\uCOB$^=@#&RcOOkLo %^dGh\bUefHkTO=3_JG9(n0).&@b/RFr0X[;@J5I+!23jeTIb'4+:.WI6=kdG"X-i=#dkDM3Xi9*6_s,].RA`f&[Au:/4:(b<Wq\' %(F>#: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<mi2;2*_(TutnS&M$i,$?`d0RTtu&=/b_5d0'jM_ZH[> %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:D5<WZmVK`uO+bf&;*LkQN5F[B\9a8u\d6Qrr&_F'209*)Nd_Li1U;^&d<_Scda>9UcI %_?Gj2@j/VR_F9ArCE^I[_M,Vkk!Ff.`5W1QHSNS0_?Z$uK-A;)b!ulMM`6r3`/'AoP9J+)A7U#W<nWtX03-K-\#7*]\8b,.ronWi %l@mD"nm4\<m$19a12d8TmY/t+;1Crnn:aYci?#b=PV8!X$i;rY8V_]YOEhjeZnHgp$hl_nM+<6iOF&%2e1Z4COS^*)j=cJgOF85" %oHQj*NVssa"@S[[Z2R"e<,b\l\0rI&=pZ!NKi!CF<,8ul4+o'pTjGZ+>N2AE&L^[be1GQ1(nkP2XKN4NEJ1>4Tu'<u"qp0T>VnoZ %4d&XPjJ$Z-$)3*q.2S87:m!&eDRl^Y"F;bqDTkOZ-ISXqQ"t7U2h;lBW1.kYfEq4q!p-#cb"j,c'IX2l<JCQDU/8JcU2q#`,),;R %(SrFWMstSO.M\_V;HV'W]Gi??:H48s&AV1+^p&]^7lCS%G*8l$0"e8dm<Je[/k6gRJ9Yd:f0T;,X4l&lR4?^j$MCP'AZUfA<iEHL %;<A3C]L'<8M`+\9,5//u(+PNe)J680fgQc1e5-PGeo'2XTo*'Mp_KmIKfX5jc+/`r?)V:X;8!>Pnj4qAMsaoEd2/hY?'P@5Ohadf %n=i$i;(2qH$mY/W:a[tu</ed4M`^Ti9eMc1q"Y+7Q>^f*p=GRhdcCi`MH$J+$U;)q=a/uM;!%7R%eHEbGuNCTof*'@c;<[[<!9n5 %LPAErOX00k^%>Uo1fQlVWi&<QX7(qITlt4Qnp8]9%CK\5S_8oFm7k:f?0O8s`4bBt?F4kX<SiXcmmZV&e-5h(*uq9hLO-nDG&aI% %*"!ls[?EtGj\V!udV]P;SjPs1X*XE]_r3&'mjBQ)`/<#f:ndmuL*WUXW[%jA5;3(OFBVd:MTK2_'qf#?8OUWqa1Z6m?ot#pYsIWi %qAG3)=,Hr5B?tT1oO5!cNQ`na&D4o\):a(S;9U=@X[$k#38,@)TJ2d2<a4.XWCMH,6f$Gj<G,iSa%)pO`(2&@G3IgEe"Q`pS4t,! %,r:>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\&<</k+)SrX %Wj&d)A;O[-\meR397F<PD0@Wp>*^pa2din0Tq.m.ko'c.0[JRn6'c[@old,@m*fe.AqCi(K4\LMVB!F3r@EV>eOCUU$0b11dd(0D %j[nrIA=a`c6mYJGi86\_jMa<n(>rF47)DO\P\MX;%*."%p[ANj/?5G(>Z<aFg/:K]Pf;o5>A8j`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)<Y[LY'pf7DO_80]"5deH`8XtM#m(NSX*9F`)6TZlPt$7sB5.hnd)N<G(Qm>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#<oT)bsm>Y]<t-6AMK7/Y_aKK&aD=KCgcJN#TR_P-Q,r'!5WF) %dB0DF8WqoWP*uZjCH;od`CuiPln%U)LbJ1a&/>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>g<q@; %o3h(mh3`pT3S$=c8hIC&o\';^8L7UhQcr2)ZaQLsm/^'`]j",db9>K:2G9qtO[-+HfuBpfFoX&$O<B_@DWnoIB$]QumYSN^^l>cm %MG_UE/sL.FVip842[=[9?>a_)m62q3ZO)e[9<$FO=D/6fThHrWK'BAhr->=A#l<Wd3n-*DK/=54ScTiJ[_?l;-R<Cd<BrLL:&,ep %\9'Z7P_FNa+T2d_8RmRh>`s4V6#<a>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<emjm'h5$4#s9p3r':_TC[FR5kraZ&FC;Mq"i0Nf`bcNQ.^0]+7_G?t="d5pL'@>^1> %Zes:+1VZ57g"BQ.*BdBOPa&Rg40tle>01#7KpKoG.nL8R`m!EOOCB9j=kaXhL01p5WF01lAn(kF9%eJS0F<k9<`J,?[H%QN\;nBN %ro8F(X"\Mt4^Fud-HfPJ!uGXN;)ST$#Ua/7;/\qB-_fY^TJ90R'_$7UT84(-eSR;O1GZGOND./-&?kFH.Y1,KK4,#q2F6$FlW011 %bJ62q\Jkf?pkjN7`j]<>-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'$R44<Oet4Ql:X,'FgsJ_'HcZnLQP0E,0psK %5#OgUcCS4ZiO-qg)cI>K'iUA&[H'MG<Dagf<AZS\](!-2nb8fV=/f=6N+"QiP="%,?mZ@?WB=L.(4\.O0ac)FY\EHBo@AJsh(-ks %iOLX`[]::;ocRt_6Es<L=S1g>P[Nf2cYPPeb"]m:BkIB4Z.O=T9QER&fF<^,'6Yr[Xjk/u)orYtXX-RW:4hQG]/%I*4r/fG)hEu( %.`95;!K3+<OX4DMIu6MWB[usT)XHUD^WE>+%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 %<REoRO/*Vs777^b"iY/(R)jN&]Hr%;"X4)G]Ve?gXHN\6O5)(XfG#?WrQ*.=U+Tmn8G_E]NOjVo%C0b_DG(e5&K[8MP>YTZaP2lc %,.UMf3,=N/eC9Wlks4hD*<AKohhNm&kI%j<>;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[:kg<iTBH\LZHVuLkJ(%mD%&Q>XaMUijrU>!?JH7!=0YnuYeWWfitI\<,QTt7r2 %gR*@"j#1YW)I5?hX12aEn%H6rSU`1elRu*)2P[<F$7['!AOm/t&L)uc83fmA.1dk+HYDpU/DZgU6$jLb7Z>-!l*XPU07uB%q0"A! %>8f4!Xk9tQRV5Zrf<qG3lFLFpdE8[<i0,6f`DtM^@KBiA,C"pTRB>#m1T;J$$*@kj\Ga$f<a^.5!MCGUh\oG!)U7g`bRB:T8g1fY %1)9l+Dl!3nKghPB4es,U#[h<<R.A"pE!I"tD;T!aHO>%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%<UVW(mp*J5fL;p5U5l)Jc`Y %dIJ8F&>[fqTs$$k9pj(rWdZ;H!A^=O)CH8(9:>V2dfC",P9<o+'+#c&-G^rd>fdGsB)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`%#,d<G(WKA*uT]*bRV^OWq'?8(]Z6i'PU\W'$sFW)]RDQM+AbboH`EVMue`>WH7JuTPD>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%<h7`@[;M!\7O^I.OO*A3*s'cqg4%*DPuoqQ\?-hBk!dFs(cU9MiWAQ8aMo!^fP+YB]cQg>;\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!\JFO<MCGT[l]F#c?C5VH?B.u2mEQHgALdK__`'K-iSs;Hl"lTeKhH\pTSJXIkr:SBp$;nUiK%2+<?;LI)^<iCU8a*% %Z1_@HHmhE@(/CFWrS#D=QJ0:G6hi#d7he8'8Krh>BaiNHkkHQsC(n?ISF,tBh@Z^Fj+;c:E"Z5C"f^&.'5GKj[Riapq'T`(:J)>2 %[Sq16F@!PAn0BXpPj7nd_XA]\TebIY&VeHtkq<bd9/@uD@'J/(@1o5"@:`&0_@B3%"CNp%WL[*@IV^,AIR?P/&/5/rie#@0+WTrI %NrTpccd(oZFoi5S)OAMaF(sA-U9Q0QL!D!kq/Slp+D.S.iCo%r5S-u1PoiA%X-6I*Enk/q$6Y.).PPK1:uu$uUu@RXp5@os=G@(h %(4S+rs)pAMrc[t]6=2@tF+:S^LHG=`?2kGjJB5]We4Da.W8`TYFS_[JE%kL#S0*-4Wi3+eM7aBE1_,c3qmXl&n19SqmB>^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.<MD">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^<BeD_"7LQedHZoto:$,\uVQO!h1E@E+4CTS<!.>+>#)<38l/Tmn %'K#qskMdMp+7='lZThk.)f5g3\M*?^0<Ms@VVmdFig49;;-3=uIsKE+4D]n(l^o6*=q@+.?Q6K[c/-@Hn<Xp]@Cu?>1&[r*IG"%< %Zd>jT6mE"[M,['_3=TGob\"!0Pf7.i>P==S)*nVal-<=+"KUqr2lHs7f-[l&7=[$WOQdkNH^0RWjRt@!R'#'oFHOGT:PmSm$<aUq %9!O8oVZpFJJ9K[H=J)#nY,'i=`ijOB0#=7?<s4CIpJXcGk'K@fC5^@hCb3]4Un!4U:5ZT'acQA`;WtEr:2r![b$=sQPY;L7V'2Tc %G>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!)<!hJ %bc#n-2KOTuJiu@dF9-q>H.$P#)3#-I:f'(#,fFBn[;[u,WO=AeP?.qK)=<Q3K3&IWF2Q;1*;;XabI@Ye6S*J4)S[K?K=L4[TtNm9 %+hi6+%u"o>"?;#C/iZ#XHA[*,iFduDqY6=ZCT-S<C;>RMZ,Q9hL8TYPUukB0E8YD*+]h]8>!F"5WLRJ,kWj,m?Pf.Tm@_S,2j3U, %&?8jl9f<)8^%^jp,2JB=mt2I:FSA?.e[J>A($]BrDI?_)GAUL<M_mdeE4DXTlli3c6ej5=>U^5.&DOlFEHH-'[uWU=/dBdQ_qY^% %mij7E:BS/(5YG>?h,U$/@8j2ZG%Yu\J^M<A%7Il'To6d6,Rc5B,!NJiooi7:TS^K437?(M!=VH`.lD)>D/ls_I]8h@T)7$.OpNEI %kS,^Dd7tE=r.-44Q5>YD;[0JSET-JnYj9!;6M-l(FU#H4M*>@FHe06]V90blTKl!gL%(s6kGY9EO,5-sWXQD/2PN0N\</ik^>$`\ %/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=<ht9o>RC`mWi%Sl?`;YTR1(klrCs!iV(#]F0U5:hM4*pVO.R%Nq2Dl!+F#FS %;_+#f<YN*<M]&p#_;\r<T#Y!(%`"Z<0_VW/-N82GGl$)9bunoBb>&4*?4a@Gr`'D+_St\]IeU4*lU+\JdH=Ul(GKHjTOr:)?l^T( %CWkq&5qmGcD2d88hVP#'PF*fpN::(Zef9E^*<c$u-_o4XrTP=r,I8J,p2dK\U>-1$^uZRmqIr5J?P(K!KsCa<pusBrHh.nT)U$!% %DoU*&JJn*9KKr.Tbr4r0Va_XE,5pua-Q9sF$<-t9pe$m,AS#^:`sNW&[*lFE>W1rp-c['a0)DF8.l/k(_+25FPuH`hDQ^(3IHs*\ %5bs$<-fU<BE9fduj#Ng!]+F8'(@$_9fQoOR&P!Yh%LS0%UJ?h`Th6NUk"bnMdqNkTk!R$Vi`d&3`r&ciKIqJFP1c0t7[C95:"\E8 %^LKZ.SLZ9pB2ET1M0Q9s]kHQJZ5d95m:CA@Rq)@q/?kuc%:L!E><5gn[87^l8=tc5fVs%di*<+sUPk/b(.c]0!kiHAeI8q"e$Ru` %)L]^Y5_<Dc<EsJ!D65]/,]PZ<q#dfcPt]$o%,[JG3H_o/]MISo9`5t;q[%'4WO=<6bQf*@.<QWog,C:[5%T],%Iq,5/F^%CV$\T= %8$\KU:N>>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-(QaWDKA<tt\KVIt(b-p&5Fl6:l#\sq-lP&0>eu^Ss:ff.s=7Q<T5U(`<f(57.2!i5obg\#bTd3Vd?53cPKq_\CI+C`$/!T/, %#^D&4?]ctn76iC<)nQB>"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#8<W4b>ap^]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+2D<O5DG %R690N-E`lD``X$$pu5aVjg9bT)/-Ol,K@-r7C'O_TP:'`":]h:J7<o^>B;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(<!#/iE-C^8P1e/UN8Q:g)Q+VFZ[3QL>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\==(bX<!JB2NgEO&'V\K&;R<gC>28&O9 %7]u&F"R)>Ob(P$<j;ZiNY#PZ"KRAJTf<msGd_I7HS$Bas--O"DEk8DiTk7Pd1;9-S`0oF=cit':PSa(4qBWKtq"KsRKUAZL"-cK. %h0U<G!X?%>#=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$fb<WT+@MK85%<`k`uSb'!YR-D"f-]KnrJS\1a!G+7<S52Ud>2s_7[Mm+b(uHYARf(=H'0iCUq=99D?oaE2jt*_/7jS?2(O] %CT4Ud#MOj(S.NF+#5.f(1P>eUN<h#:\a3>9o;k(siP'dfXIbpu7R@k,"aJ4^]bT3dWen?#4e#FlUf#M+F@OLhP#pn`?,!mk-8J\; %FPR[+?2E4ZZr''Q(tl<sZl:J`TTArQ#(erb]c6Y6e3p`eU)BkE;_,tNW0QGLjm^A$iSqQrO</_n1<qoi_,Z&DALZ\^7B>]uGls%e %[<CtQr!bakeXQ.u07+]5mGWLL24(::\LOJ=Zh^MF1b1f9.5SR30GJ!8ag"^.A%kf7<rA@Xmu*W3EjdMk2$<KW,cf#W:"^LMn)YmQ %r+T`]mZUbc5@a9Mmp#T_-@R,L9?`eFL!sKD$Z5;CLlF7-jc>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 %"\c<j=9"H2;e`tk^Xi_<fme)DHZBCKe82_2WU3>V0WE8%C<&jArC4$$X<T]V#-#Rp+N[M$`C9ib7YLI8K=O#PZV9Uh,qt2KTR+;m %H#=+bB(bKpMi:F(I>hPg'4H:r!!%\N4FngFf;rYZmD'>o+]=k%]W3C4@0C_$)s>RU*S$oo$+,3bXF?6^f<q04[U\s0q,;m4Wp`W3 %eqab:%bi\h0MXgIJNHCOUJ*d$_K%B*;se9ln?Tb*I_mBN/h'5IhD[Y.!,mM;T.DJeh3%fGn5584ISa:<Up5IOP['k=^9KR+b@7nc %J*MU0TutYmN$&t)bSPMUdpd,hL][tq6Pjjl"c;l3=h<O\/l8JP13GlaFm/X;=-QtSnbZuIYF$og"IBjDG_9&tNI1slDC]6/9i`#c %mWqr]WjWs(hXY'>)4O+Jj<f:JN0&hGW_pW/.4*W%cnM3Zbp5e7>3SmpNX/M<X@a/-_N0N8P:AN!p^ruQB,7kt.Lf<lUU/NrB_P,J %Hr9`P;>PJL)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)(j<gu8eB4Z$d)3[&7h?R-iBdNnV6[NQ=KDAZ#/F->JHN/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<S2 %$$a8p?LWXL(,83/?4i4<`?`c&i09L^@tJa7BX%>'E1]L.>]]"X;c-%W6Ajff-,(pA"'0n7ZDFT;SY9WBO;VD`UN0pK+Y62+RG3lS %&nijD<D%2QGl]<qC@#6!Y0_i=6\fcS,?(%bE=Z<UH`'08XPVG&P,Cl3%?8O."gmq_-3<>E,$@NtRmfcR[$]7(k<FR/!f>u(9Gu>K %<(_5nHYofqX;fu^.,cP:WE'<fLU5-riilSJTp]mJT'afrQe$,0d1NTsdOiaJ7E^8`>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@[[t<j9[/nc#/\GG(YuhBF:,ZX]"kOTHnCM\>Xr; %KraPL5.0()8RB+PQ4(.3[,eh7Z$h3p[l@>HITJo9"5?@Q'+ep$\]U5'W<G&.b"0GIaYp3WfF8&^:5ZSjcPu"e^h?0q1gJtG*Q#DS %YG5"P"T9NTGAbZb<YZC0EC4kr!J-/&q91OZ^)bqBWC7>O<B]:F-/S=Ij'`.bY=Rd.Xt<,#l4&(S].<jGna$;?4G#IS:_FO0U,1!H %JG^*jFijHjGkbfUX%eV`*rZ?K<@Q6h\3=S2m3"5cq]"C.F(V;VVK5nYGoWrU7R)&%.9&tJo!bBAlCA$kFW'rVIh*<G4I-_$e^9$+ %RWJeC9$3G$:Ob!Co.%5)n:sZH"D^P.@Ab^!ESn"a(!T@[oWcFS'gR_(@,$G;j(05G`G)W.5N!A#nQQc'eg1+$)5,Jqcmih(8Fl5- %J+&f\Kf4=Q-=<mDp$S3^GM<c+8npQm0DPqG,7+OErmK#CZL\[4VU?MGOlQM*dN?a7=0$#7A\'HshDSjcSngppW-*\3UBh(m'"f.q %$n><L@<.up9%T8Kncg!edW$31U++,+>uif^0YpV1nWksX</6]X$28(0/4GZ`>Z/s-72&8;c-^O1q2t7fZN&[e*%uRj6Q=PA_H4pT %%_I:"2OOe#E&1K,\Ibr4l!Eg'`lo-80Kc^fke!/*;SiEkWO<%#D@F9GF673-B#h<K.ujsDF%rsY<l%si[i9FhRu6[On`l0g%g6:Y %\/]PM#F?hZ-[LhE'!&gE6$K^R1+8[9D<TZg[r1c^1_Ecf?4<9h4MWcn7SAK%1o#F9]g4_dl"6ngoR$ZOe<EsB([ul?/'$1ulBt#e %#\R-4@TEq'*n<I8p(b!(8_m)Qlq*.q[dA!+A`4N_U6ha7Fa:UZK*G5mSk1`V.pI?_6*Xst(giaM?/S%'RjahML3JVQd!aBt/qnc? %`?)F(&,WuRon'ZfGJq6K;-UMH;b97pe*a:gi'c<^3[i//8hL5VG>G"$_+\*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'<r]'5p*qF-DBU>-f)]Z5'^TlPOZ0d#].LYDD;Q2=)`5Ldsh<VBogr]n%ZeG-fQ %!sj2XKO"eb*=7>c"_"$U92C5FImVe%iN4WJ&,8V9!MGtjLFFgdKqLG_0f+7Fng)oj@dqimB8Vm9RZs)b.,C6QTmDt;H7i,W0dHW` %:FWu(G<s7ZR:UsOepc3J8(&6r;K?.E!LkllN/!3&[@<T&?VG5C6Bn1^1;K"icPUm?0L_0DaIp(Gdo*+Z])k6Fid'-nH*]_S"(39p %a0dm,8>)1*2pokJN6P4X&h=o]Glp;dd^ASCA+fQZmc!U0Z6!*Z8U#="`[6Ye9/_RgU-9S)<JQag(\qi41)J`3WNGS:D0[VHq6^FK %8[#pQ*gMh!BS4R*oN4g-Xk-b9%K861g?"C'lib;"%o:+&]AB'3QTML?"[hN?LF#%]V.U:s[qYaf5ZN8!FUm?Y&^L4[ao7U8Inl<k %U_lN>E]`.'9,"YPW$i5Bb/hf.&S?JtR_^`h(ORp:B%FlI4puc#K>pnnalF-5%8?Rf"MOhg3Ip@LJGqY*5MuhNKS#S<E1n]`FG>nP %&5XVr.`I*bEMI3#A<%&f,I]IV(9^;2?_dD;=3d20Rl]4bK`(c^;tJ7;"NpL:);8j3Jfq.rUVGZHIkON6R1;KR;<YMra=Q$/'Q;KZ %[n6-[%f1i9;9B#=oh5lMXWH"78o<PraTep!7hkP=pm;_?k(a]ZA.%5);>g<A7MQQ=0''*qa;%Ue0(c@UPq42OK`nfFb]]bp`>#kE %:)=Q3/gNU<Ti;r]r#JYr)J!AZl`2K\6'@dsI5njTUe\T!(Odf4(G`UqWj**s[_*R!FCFhE8Q2q%24rJsI?8Znbk8E1@f2*sH`?PA %D+_,q?=4GF/Ok8.jCk[VbKo/XmcLOtmc'3>Sg8@FSfs/`HjCD?#Ij(<ZC)aKE_U"2b:$J-e!"2!6ck)U$$/<F@D5Q]gpa5XnA.3: %[*j3#'e$ZWnRmn-`_8-8[cls)1ca$PD&%,qA/MZ5U\&]<cq)o3E8X8u0'9-PN3I@Pd^2gaJ0I5.3siU8Il']7N7Ujm6[!:eUCB5m %+[>_<jF-4hL@!ic7ss88L[=)``I)/X8*oH$7t'?$^QYuk%ek\G9e+.C2,uJlnkRu:KpIGfI6V15)(Jka7gn"7"?%,AaHm05cCCQ! %/R-'[8*t#ASR@n'hfpi5D#s^g-H=?LJcH!2Gou,@c(cRf9\'_QYj`<r''27Hfp.?nZ!g?hGglb.qbJ@79Il>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'`D<BfYEVMS/DP40tXN.*(fG)B@fAZU#>a5.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(<F\1aj5<M:6OL@FRT7W@BHh:GGiRgENqm&9iK %Oo!_`oVBa1W7C"#2[ia7]Q$?ZcFJs1)MiQGF<(ll#UR0,ao#;/'3l)%,D<,9A*D`W)%k["#)s*Ye6d29G(\D!fb91p;uktoP(P9b %opmrlB(@Z>mja9SfB4O.F+]s%!'A@]rU8O#JB)Y<I"^jJ:o?6OoF9<"hfW96X5VPOrIgSRLB:%tAnbgH+E?b-8\+0IP<;ZD<M#1W %Q+68E?.-1)e*h0(b-q)YFj'1`)4P50!\mGKm-)R<i#eY+lGm1;2,n%ui49"!GQ\esi6"CqHAGV:+PT6tI#+-(@-]I*r"-,_62.^> %hY'F#]/V-['4ha!U1gR?.K"Y3gmiH.#<F>eajM"JMCknn-`=#uV<"OE2Fb+m0mL!TR7ZJN\^<R5gR32fSe#Kj)jgpl0&Y-8VsZrs %S#p[;k%9]sE>VPi:6j6uZ?>c4l@T0s4L.`<[8F92572';FKKD0oqD0-'8%g.g2=,9*7"h/Q?706M)$Vrq2tB(s.iAC40:/<r[@.= %9P,GLS$q?X[t2Hi#5S(+J=_8DT['=C>!l$FPF4Zo_u;i(l35J1jbe(j"2<fH.]=BIM3(.*"#nlZ9[38Z*t$i#S:5X(eU4mRflHi\ %"I85lSm;g[HP'I?:u&ETMb$$_K?/'X867DIBHk'SOSi/%%R.><O3BPK"7u#]Om+JI-@A^lK47@tg"`na*VptaEn?m*gHmU]8BX$k %4>S`,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_$3IWq<EbFT8/FpU_:T59:VE&U^?Pc#HZ] %ZmC(:0A3nII8Ug&U^KL\!RPIWkn=>oa\-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!IGZliJ<YRO^CLckVn"DA2_8"nE$I/7aeF/@-iKm@Cj)ZJ7'c %\<&cgZKs3&]TBfCo)(,1^lUbt1(>n<`!_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>VNl<pp&*G/X>6?$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"<jFg^8;riOl,X*!,8cEGGTQ3#Tdb^lQrdk6&GgTRr3&db %"JDPNEBS(PC%Ed_\,1E<X7J,'[a`GFLBZ#B.;Z3G9Df4g`9%&3gEeg%bG46=Y7S?ZhQ02FdT[p!lq#*cfcUE-oZ-9&p*I<ZIm</i %P$K!1q[TBpgr'V2!uEu,`l(r*Sm/>gdAN2")]D+$mUW/u^nLo(ec.ZRB3kO(R!Y]pMV*m7W%XAD?A4^+J+2+^IWZ<LMfJrCRQ#P< %+2)'<MjFh5+_@u'F`3^ZMgSm[[=ufINZI=7/M>3INT_R\HR%a0YHQVbr868EWmP=\9g(<./Oc]k"+Epi2rIU_6JFg9H%SLqeoLS0 %0c+:mZkQ=1L:ELD')^Jq6sZXH?doPLprS%u:5gn>'<O6QU,dPkAYbW;2'=.hU'ulb)(1/&SVr]B4c+1*;li1CU$(_'$T/`_Q$igm %JbKt:hQi9N,9,@pJs47h)J;Q5?[Jo]ffM8*$&j=Z>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)i<J/f8,s1ecpH6VFR1m8"Do,WcJKj61-M6j5g*!!G>IK7XmFNl5sQi.:#5jgr6;k9 %0$@[W6h'9#<ruAMp@E]6O:&Cmm]ofUoJY8gje`T$RRGC*%hQCALujMc7]e;)5%GImo7)d=s4E7C$]]YP]UYm%U!^>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@!Nt0<LpbGNI[atQ]:(7k<ur2gJ/OW`Vlh,_S3O2*)%b#EtOfH"_g<fRZ#Ba.#E!WcO#;KVC1B7 %Mph#ZIZ3'tU?P52junrWdrbFS=Z+QU$C.><W`#Ml<N&n4b?@=9=J8XD79(j;i/4nQ0"P@%bR]^^!U_8EDrW?Gi`T;WL37M5JL!N! %>gf^U4"j?Fd##ogd=Tdj"hpqJ5cU=edj;plGrh'2@cVRn`]IWNR9b*M6i87@N0O4_/#<RM)3I'#m,jHG70fY>p;W>3[gRGcba#g+ %p3,J.rIGgd*f67]cB2glQ-sq/?Y.&?$*d<C/.a.TcR?F\k!)^_$/"no&-$!hZiB`dn)=hL0`V/*mLW\ChDBV1n6s5d$M5pEG1tiL %8D$[!PV!1E:+c\:l#+:I`_?@QHLnF2+Ar]V;#Z,--X\ot@(i\!YFJ,DI9-Jsjg!3D]6b?%oq[sCeTcNt/d!P"ZSR<_#MQ'2g2=kM %\q3shQaU7f(1CW-YtO,^O=;,GC.%^^FK4>rQMAnZ/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^ %$tp<F_1rk)8<P>Sd&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<I95r<p]M_kaq4a"h1.DF)<&;#j6"V]5J'n@=O$u1nAB4#8k:7@V.1Ga %:;^"[IDO;J-e=mon1u:ND@BEgjJ+F'Vl^[3q[mg)a:Qg0(3$;0ALXD1iD2JphV[\k\*GGqj)lBsAl:*fB!:Udn>*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=<GGHXOAGUd!f@gCVJ*I/H=R,1=,[j=r"OJgqXJlY3qfK#eBugZB\='M9$E7#WaM7oX.N:s,,RA>PQ00D.5T %J'Jr,^&gK,#!H0SJ"pfp-#C'.->%VoLpa[*Vb"/lM(R\FO3e<OCJ-YYmm@g9cbuan)TL=<Mupcp(.1X=M1glM8K[a^TEcdp".eEO %;:%`6Z&cL.e4hbL;O<8pW5;[ObdXWp9.9BE$Gp&d@h;]kUMt2rOZ&Gp@A@sQ[XV4>+)eBRf[+H<T:4QuNXHA9DHD-!3o3`KfotJ2 %DdY2dMKIZ'/blK`UQ[+WlFEkiKF0trjmoj'cp&ujRXYOl@0,h-`l=7A0[7GcOA45R^H-1T\W\Yaj\sa4X9D1*GsAI>XFqB\h@Ii& %^N>OP*@/7[<A_tEF)2M;Udsq8(S"g==_J9o<d*jM?M;e7ENIkF#_C,Jg<4nNilPh_j=;:/Ei^f&mgl.UFF4(20t7`Z,s]aG\-=.a %dHH7*Wde;Ld`\U/SBl+8g%WJls$C)ZI<g6MH3ZK+OlFhY*U9LI'l$KLoVp^!;Rl"p$T(u/dA"DD&)gh-mQ,8Je=U`,$/j0LCQcgP %Lta<$m!h(L]N<8lR;OeMFAXXMj&Yo1$7&]XgG`9j:d4V'[2[9ZL'#)OfP`/H*(pQEjG*9$2QFkO1M._GAaPsa\)sOcMUpeoH=^8< %-"rkka1efGjjMXT_\79EEd]`^9uM^s$mP=qS)K`NYZG.aAU&eoa^#8T)lkU^^%S66XD*OT9oVuu\paasC%TH=]c]+`bH"cte;PEu %EHI#F)g(7?T&cSEH6F!NS.G6UO5JPZQ&S.e<CQX0>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<en/m@%\)[>: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.iC<eokh3!>TdsMaBBW-[c/D\=n"No,Q"Z>i<k]$ejkB4,hk&cPcl@JSo,:O":M;K^G9De#;e"WQ`'7Z>ou9@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>^<qIMq2bgW^#[eJ4&0eham;\bq"bJ*S^@D@5Vl]V2oK?nb0R-Ju1,;U'[O+_PNSpd.c_O+m@?K.-s&1p*dNE1MT+N:mb3o %V8H2ucOn^&b'_KMXr]HWf-/B70%=`+F_NOcB(Q]:M;R9_Dp?$\@1F3074lJ2mDn3eARSND9*;Q*=^'HYmF$S<CtBF\G.TS^$QI*> %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<?J+m1D?+fU_PQ&nCs*oOIJ,\uI %n\!e\iP54Js5!P/rnG3ej1kPRs7u]j5Q@X2peUqSTDmkXc1l5Wl2UOnrqaV3?iRaQrq.?ss61VT=6o</s5*aEf5:Q4l[Ss5IsI$T %r(m/P5G-oLJ,P`6qdoUW^UN,#LYr-Ls-eE:8%JJGetfs+AYF[XULDr=l0$N[Xhsi(r8"u=J+crlN;rE,HOPCoIsBhR)G6&T$b&Ah %Pbi!-T*D\urgQksIg1,lq>(LAb411(,*b!Og3l+_fp_YB`Ccmi@8=gIP$^UaJ<pl-I1*d'h,e/X-(_F'n.5G%2;!XGRmB%,JW5"& %GI93))KDUL>omb,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</#AEjNuAt.N8:]f-$kl1=q`85(f+Q4`8=q?gNS@6*K,h-Ae/D'Q\\>$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 %<eqC0r';6I/]0Et.jK]CraHjAnNSd(:.9,LdQD@ajeJn"DMQL29Z4:V>GH0tV0eD.,A,(XLl9\h*#f:Rm0W8qU1Kq_(?N&b67a3' %iF?Q#n0cGqE6=us2C6AUHndP-huS\$Y=Y[KZK$/P4C]"BJ:,g4/]NaBnh+cG1]<SGYn4o7Qh(?65M"&@(F'I4HH7sQ/p23#YZJ_S %BlsIK@E+EW`k\"k@YS9BScXadKkRl7pGjJ,2LCGjp]kpq\b"eg6ao^Idj1>^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<WA;.4HC8s>:sb?Q!I#UPr= %KXn0jGT&Drs+<<_ZdL8=C<RF)_=mIob]VFU.:Yg^;7*<SVCWqTTBYLg,;;sHIn,eKlI%>\\;PUcJMgF*k^5PZU?%'9JFGM(S=Nti %q-'j7mRl^]q'G>$MA_p70r'CM`lt@?N09kl]U0PL`#u02X=u#cLN3`r<OXq6\[%^$_!IO&pEA[?I&@E6Kr8!G/W84dGA8gtk-*-* %nc_[M;1gu`Z'AlMTP2$>mFo!YS1jkYd[Z`:h;(q35A*p'^+IZJV_HZICH65EVp/b!/\/=e%>N1?FNm\WhV)bY562j#,(ot5Ql;B- %c,H#<a&(2s.b;o9Qf"Ouc2D5*_A`pT4L7O+Dngdn.oi`uKspQg>3:.]L%=iEYI?tHi+BkLnq5lG`V8ZWO7DW#loA$LZ7`eHP+=dN %:=TS_HGbNS`Egh#qdk.lh#4L3s1f)jn5a%tarA"+m/<<&Ad$lQ\R<Ab0SW8X1AtCoj.7-bcT:<O3=a""DHEZEOf=>0)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])</"i/).l9$#k-L_3Cm,3;mkj5O0-HP#_eR!V'B[p%TM*-l.+Zna7\$=2icDOjQsCna %m;B5T"'b>qd/.fGlq6W':/:*2,fG8D!<e[W?X$4Y!kX@*N+HZ1$Ya'&oK%27gkhI)H267g*-d>Fi=/VdO_[7EP.MnQ-&2VAqA?<q %?%j2D#]M8]&bRJ6H[L<Rbl7+HA;fPpEa$m^c$f1lBBNU6#P3U#Z)*<X&,[@pQ/j=dph,Jd\5]l>-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+<VlM_Q$\n)FBgr[f4_Z_e=!bt(Zc,fdEkBQ]ZFQEbD7'bkpFUJ(6g$O?EEH!(&'V_tY8j(<7S2#`72C%MLk#qN_dniR+e %MWEL1YS0XUpHe91_<>L)U[q&u@8`YU!kOXfbsni*Mq?CZJH"*`k`:*4dVESS%aIBsZ6/UXrPSdshi"g@q*/9lA5\1(B12@`7V9m@ %Mqq*O<Lem#Rs=X=n+H;O!$"V)4tnk`$M0PcUGR>049^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>q<ZkLEf#:u8\d]S9fe%H:Nd:g,l-bEreAU5X+*W."hgc0@Z8dEB*jHZFMp.%lLrd_Vi_&0 %*G/Q1%XAtLA!5X)BWc':8FY)$$Qn=%)rAIc)$4Gr?BR7ooT_Rim(+1dkt*P[$;;5Afe"?6,p1*i96sY8[i14!?0j7jI0r2nBeek/ %Bu^\.QK(hhg-DR@^DjdFAgG[ha!crs)\d(mY%8jV%23)Bj9'X.W+a<2b&1d&D:ZK2'c2=ECgpe:aRTJ7<^qXWG'2?,>V7lQl_?X4 %Z)(]"RemL&DkOlrRpaUX&S%])8-*l#&]Bl25L'eP!^oq./T*a<?/1K3eWbNCS'e2^>keH(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;7b<c(RH)>Ef %hepG=kTZW7%q<O";-fAWA3W4oj<&>cNM&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/&`'OLnuokmeA<J3m=(^!a=bc4 %ED8C!&%\HTX]L0UUr85:.r9$_X#Js'kgY%so;Q0)F^Nu>X*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=.nFsU<As%<aW!`Xnm"[_'H[1?;.&,17n57^-X/jebM2o#M$'A,BL+0E1Wujk`7]XBKUY(T<)\9 %J]#6-W:sk?"slJ;(Xmfh4R@U#(-V%N01&ZtG_>8"):WQsWJj3C:>Y+K#HF`BCjrV$m?.m0-IqXP<>If-4VahCFet]a-dQo/iND27 %H^oGlbiu<n%W>)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(+;+a<u@fq)tc3\;!g7tDW4ZlE/ea]chs.m+/jdd3/$&u`\rBnY#m1"]/-bp>1nAjB>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."*um1NINr<VcBi&L,/B#=[;)qqHt?4na4L&8H5bPo&r>kV,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;'#mV<B0WB)m<kBbKl:&5B\7^]Q!fBg&5sg<HP'cN"2<H?8sW %4JYc8e=U'?KjLJN=S8neY;NC-OF7:NJ14:@[N>nnU"&VRO0"A-h]d<d8n_X]^.o2<X&YF7_FuN&M-lOR6aRLTiF8mXQ/'C_SIa3u %;^liP&!^iMOSfe+V67X=k6!W[h*=#hB.aa=Y^V"<QJs37aUci/Zu3(dmc)atRFNT!X7I"dbBh1)<hmXMM_o9%0fmn(4/#a%%(gjo %k&_(kmLf-N8IqVNjT^X`g6X0pEhurd75LNKB]dONk/kY)ZodNVINFFUTmEg<R,*JM#kA+^_.tW/.=d&r9d4jh=1rV..?f88aro_D %eZ-k]*^D978[Q*kJ1ZDU2-+j,\ieRGnM[4.DfE/X/q!f2)CJEFdeCC8iYHd,dN=S]5D8t5,[0>hUKfnE<Dk3B1lj-NRkT7n=ae*C %-3g)'NIJ`+R@Z#(kY8uT*"tV5D)2;;gm`K"X0ZWA+"5InmeK%*GXYfk%\MS8=aG.2]bq%_0Y>fC+ikG0a5_pDW*hRV17U[..'E72 %8(#0<m'3ZcT=p`5N$:c5>pD(U-.BLnI/dLfDqo1$@76b8R^Rnlr7_nCdG?f>Z5]:F,Cmi!VH13X4Ee<R^GuAoFW"F'gCb!do`]Yq %D(XC@h;SD!msYIQT9S4[ci<"Vp).*skMs91H@G:_EueTapp#VERs4kH@fgEMFEoRaf/Pt#6G0g7<56]?OR1.Tj-q0''OpE@:ogqs %'BYK2<WL*7?L7a5!Smi4SnPfLP&nCo(U'SPj)j2o"idDs/(#eG+NR?Ih#V,q7XGZrF$<NJle?jXK;'?XLbW7QK;NDMR;UhE/O*;! %#*qP+*TE,Tiq6R-F&^<LC'ob=@+<6;"j,!<Tpb?-EB^C4H8*TghmRjk;If!8TRqsjbjo`D%%.>:^!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-')`tt<aJk1qeE*4;_^12Mn5Eu<dJRmgKhsGUp5MKhC.V2H[j7UPoX7fM;eanTGDaD]q]>J1qB]_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<q;*9DHW.&d+B`K9f1:0"M`D3m%n3TM(h,Igd\b2aS7d %r(E,:]@dT#dLm-jUW`6o,8b8,Vi9k8VEG-e7$G"`RYt*K#O7U8+gR=rf)0BCqG^@WG;d'dbA7J$f#b"`S>:T&3\CGCNrHgd#I=U* %7+<!C3?]%s5[>GR]<P&$K0ol_(Rq4@O?>Nl,UTARR[Q",8-]Mj<rPc+1U*)I,/a4q!(Rcq.)X!U`+!i27H!7F=H0JNj\_4uBs*EU %r7c:,p!d2^ibUV?U;]^QS^<ZS@RB[[EOHMO\Ro'Xr-WN<GLD4?<<4P<_(J1b+.j`@Y!G\hNlt%$M:%0Dc(0V&A-NTO^3oc3P6)NA %\0[P4E/?Op$c\f.=BaCQ?FFu5`FC.kdBXLmM,F1/ib6V;(>Z'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^*&?$#<Re[[WIoFE/jT]h\UQIgD(<QVc=)>n`IY7iTm.%-r!+E1KMqFA1*"If\?-&dM%2$(Oji8$\&$qg/P %e*u/TH:QW/<ff)]E!4AX['L`\[RXk).cqpGH.^(Ac62mOjq>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^c<X&>EcQ<N(I %fc_b*WJU,KG,FCW4GmXEEifGNcfX"JI,&1..-au+>]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?uTYfc<Wt:E6+G:8kBHX@\KlT*A6[I9D0Cr_WU0O.*5ZBWeq37!ShFj`2DACMR %NFS:>KFjB"<K/+E["+^U/Yc\_plD`7>$?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`!8St<o)+l)NNMHU#0'[Anq[,3$5Vd`?K#&-n`O6m6Y8]U)kt;o-2O,%pltjP5?>uo'\cdB2PV<jb\qF7UP<QeuZcW&9 %]*IIW%OYR4p)(Rb+R9,pc6uNP]5!O(d[3FTTQdQ,B%8,Y5*b']bR,/.0NiEKgRTN`<AHAl`<]-kIl'qL.LQiN\-/=>/nS[R`f*n/ %WpJs/%#Mh0GQhI_]T:"<pVP'i8A>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.?<c!W+D"O#=0br_n`(5C3XRPMHEfFlgK$u]0d#CimiHNNf5:EYK2 %/N?*E)_EBi\j.<gj(uBoUpepMcUN8c"[qup1h@O>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!egB3fLeST<aZqWm;eJpIf2%_?3 %g,q-j"ee=<+[7:i=]<dgP+!TsN_us/&q<*5g0B?9TPa(nc'F).MJK8C1"g'*Ci*c`SQ*nt\Y?;^7C/f>eU\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`_#<c&9dAc_#^)i'3e3o#iu"O2%:cr9l;B %?e)o^d=5$AmXI:r`Q?qcrVkgQ/cRi$rpnrR^$5Rlk1t7Z[pT."p@dM%qP0C,H@,P^g[!$a]??i)dpKJsn%QKHIW9`1MZ<[FX508G %Ro^E8lg[OW5<,g<^NjJs2TG8W)l%1J:WMkQf5KPO^8>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?<IZfA+Z,Of^AXrk#<>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<Z;[l;ntIsm.FCfi=H %Li2;fFsVF8/G\;jqI1suL\p/RLFLGJ[UIM^me5YZe_:=7n%uDj6s!m.n&OO=!X*3^hWR`]86\+ST/deCa=6sJajA)r:AFKOpu3Y_ %A8GcNG?>%j^:,%aqYc?LYlEb:<kJ^q^#sf,7M9VgP5+@FOT$=HNd)diq2sZm2r[R)l,OCNkBj\'8)l$/hF;D(K0$O8U[ahSfABk@ %ln2XO^3]C_Msn+LE]fNAhIi\@"MOc4:]J=m+IbCjq`Z-`\@W\?3W%%c]CTqo?G3q_rpoXVEq]j5s&[sBaSrDFB!V0$ephLG>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`; %T3<cCG)u*nXPE5f2\5aQrquWXe`Fb>4?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_%<O%YhG&T-/FPNBIW\U4har=/5X<uY;"@nOcdJN=DlRn;JV,dot2 %2R4#e!d,bb#8=&ZgREOI4s(Mfg'H="60Zk5dUjQrV^$IJP,GFEViC0S)^%oq=c&3)4DioViI+DK)t=A^0@TPXk%aZsrG!S#EkEP; %re?YS*/1NNOGtg7a*C$9nh`M=QS3:F9e3K"1d-n@_$m7#MfC]cWW)X2>6"1_rHJ4t0BsWS[kYGUFnt)L6up$Aj5/nM;@9-g,f0jl %8W7XS-Ci]F;(eAU9]MKHV^H<$9K:'d3-W]b1?%kPn_N<GS)6>%\#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<Tol939(p;a5K?XEd&qVp=4 %>?T-F5>Z'DEq`cq>2oJ=1i;;rEFV19/:U(Ap\@oBq(S#WEZDgGd(qnL.JGg&XV`XMofA+6b(&o&N`O<OUQtBl[Ne9mjYlhkBVjM$ %kWa,=j_q<9q<%)1bNG*rUe]qu!nl"L2BpM[bNnJ`^O>Y(gWRcA?dtQjq*f_<Y5Wb=`V3Fmf9^Ei\@m:[`QdehX*YS7+.m@kXiLBV %7u;`)0s-'3Ugl^.UO[G#OH'Cj;*at#!n)"il;#'>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.I<RQr<)=Qr<':Uj^9;m;9oS)cR<N-h0BOc5WIL#6[*fiL=al?Z+:ljDe8\L<!*AqY'L!L<e>IBV0eE: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%=] %FHhE<EmU\mhuh%d%oV8jOFFWt"l,ld&!Gf:N;`52Z2a4V`?TuRiinR8lYu_]I!b[FW5tTdBe,C_G^o6o9u'6B'1Yd4"uQF2H@,P^ %g[!$apP.-=e#hDN*k._Yjh4QRI!tP]o!c)ps8BBtKDV7k/S-(#k4[s!K<BIF2A/J.KC(&Uk2tX]D^9Zf%<WsZ5+G.%R/Z2;S]3D_ %92Bkpl!JnVoIlh&ns&n2&7i"0]H#ha?L6NR>lXfcC+GEef&M+XQ$4/Z+mrAU5DFU>*ac,iF[)^(DdWL;St>BmFq3>#<Wt_I[0>g[ %c0j=N?!TZWR9)=SEoV"1W>kX0=tXu&Z+o`47Qm\2fZJ=QNPTu*IlAAgpkU`4Q$#;<I+ZFQ*hDfZr=@q`oS7?;2A8l2e/j),g;ACG %Qi?-@)148'3mD1SF8/VBo\n8.dl68^n`b,ZT+AE@SI9r.(I*1onR7=RZY_80qtjT6A`QYZ[MZG.r^4=M2D^0l]Ib$Xr7-5K2NBQA %;.f\"AYr8FZEYDsl:6u*2A3b#,7.'`RE\TB3XSSOU$99o*/18bcu]tjN^D426ISC<^.4l2K0n$A@/Zu72Ep++[J(n.gY%BEb"]W@ %_#6_*Ugq4=k!E]F`IRi@n+Fp9SYcpHZ]Bsl2OH2V\(<Pt&"Ta1e"7'aNP:2/4R?prSLN@e)S^X/$K3pcL8=7k^O1HXZ26amS-jB0 %h8SgIc+AJYN+,hcoBBrd50m;V93:7)C<WgJa![b/Hp9[iR^30fDs1lJYP\?<Ub:>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</o/r*cA[r_kJkp1CU[6QQ`Q5MM!)nrsFXZ]/5TpC>%>">!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 %:><At#6]<h/>2*GeK,_/$(p.r[RHq]@sPDo5DeLHr71K52V+Q6f,o2g*k$!?Xji;Ss,nV])>b)*Nhie9s,`s"S!h1q77[@/c-;0] %hHk$[<BO;%3W7suB>=<;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?/T<U;QrtC,u55i[D8TQW_bIdV#r'0irs1:UI4,p/Q48j[/^Z"egFoNO2B4YE? %ZP;G?3p2(n<L2+#'jcXKos-u;QY5nHSe;g1c#LjWNRJRZSEa1R7,[to/9R?Cg20s?6u[HQQJ*pAe$G2FG>KYA:\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,[jnk<F35gCU, %iE9>R#0X?Cl>3;L\hui`GclCYdD7O"76XbHXME\-mnUTj*p3=20T<T%TmlC*G!(LEmDJ^8bV<T=0sAF`feQ@1"sM&;b<CSl9AM5V %a-b#Kp,X`r7PU3&)`c5;*PoTEVe8AQY(M#HrJd"i/CmOV8No&O3M6;=gX_W"]Y%Zt<m!O5b%2%anmnPK@@BY'5o=48A#Su*L#DGO %a,rSeQ\PO^Mp$m5^etCn.@?EIPdl@Vc=\,T^rsUXQG@fpP0@`I`m[$$)]LNj/Ch`$'DPJaEQXJt'qE0*3/t3L$D+#F\`bdWENp"N %a9OZTI7i'VRWQ&ZN3.`CN'VWm?6EC?\e-qMrE,"E2I]GS4=3373TWi!+\'[lQuZ"We.%W][F"X8["D8C:sNrA>PM<ZFMqj4lZpUM %I_>k876@HgAt%2KmI2[Y'<pbeW)jHG_5&;(osg;2aCpp'Ub1E[-1!5YC<(EHbP3UubG_>'W#9MbR;.4la;1IO"I<\/T!I]N)O#]X %dC-<gN'_FEP3Pc1ZX/K7N_cM'\;^9hZIH;BSL@>^WcrUDqc>Q@?Nie7(EL=iL=2V`=e6cEa_Q[@;Yi52r@+q&?"05Q2kkJ"VKqHF %:RsebLnVRrFh&hFn,?)Ej]NU4P(Pj\A1RHsUL*S<./<aU\^+h/E]o&uUf9G%oo-R`\-*sL'jbLD"bC1/!`fB;!2QqQi)l$TmmBim %^d4TK0OeRXl7:7flKuu]e\."6maO*ld['3NUo832h0f$!IA=YAb;Sio_#'L5+,,G#Q'rET2qK!+"spGF"Z_k0R]"S4GW?"O&b-^6 %TY"&#PPS@U2Pa^Smn5fKAZ.iYhP0kCKtm%_Xh#iH89t0NA`=/V5-_UW\Ph0=ktkk\Vg%PD?)E+#HP0)'6QO^Cp#HnaBH_-9HWes8 %/7XhF<?H>N/'oRaZ5d/?P)qq"lN$aTnGpI3DtZ:CCChi\@&ts.0FKik"-5LmBnrdjQBtGd%_>tT.CqoiWm;Qb:F3b31u'D'dR11( %-3dbc+jW3P;NdW6S;LXf:`l\<q_hGhcY`"6VO(p6A5iYjA^M$JF_Tt#oY/WArApr8o<G9!;!C3jTG!7",_6F5:GpDh:'\JGFKYE3 %9gf5DI"&0sUkP::,pZ-@!`iK*8eEc=UrtM/]haLK9:#H3VYX(P3HJK63KDh0Y]'j?;bXuO"B0P'UmX:eMThD<<d&-:LS,9NTubK: %q,u[sO?e7dGQ=_'-;<33W)$,?H:p'mZ\%j\a'mJ]*uB6dWIkn:^*Ih^_b:RA9l*EVl.%56RBq2sL90I)T$*OJXZ*j'IgU3J/5ROr %[:fefN.q"V3f(YFdKmb/;i%^r6A"MJ4#,2c];j<cb(,F3h(6_qN)crL7TR.X-%.J'=W,4NQ8V][=C9N'nqeMB+Nc=fIju&6%s+;L %Hi'6-(p"jD56B"PjG2<--H^5a%Sp+8;r>"rOIUR,`4s;,</GBmF93JQ@_G6Y]5jJ'^jA_VZ^cY?5dRO-EIM'3;QBA?q$Ed2dkpBp %'"pK81(U,i/(Ji3p5?*%+GV2hhG1C^\tL/M*3QA:rW!]qO#WYOjl]4t73]a9Pci:kSFj#o_?JMk4;Qe+<;/Qj-)!]^??lQ$]]]Xd %=Hlmf",P)VnEV`,<?>CoJV`CRFKOi?36A)<qOHe5.*V_=Yj1bJEB.WV(.al#3nI79CFW3>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#<m?]+& %)*nqM`\`TQ8ODC!K"`XU(T$#_4s&AGqIF4:=Ga@J?M2DSp4D4*`<HrJB!e,l;9r[,!T9-flVJI>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$<?EFi(ZPP]q\'4J$tDFb=\_KK/JH<K<Dob#!LK<0Ad'PCWjn),TJ(B"b!O3]F."ai %lH@D_%f$n1huqWg:WtK=-5&hg^F+9JQiYH#5`q>\9_W2>mrQX'`Hp`Z"_+;P,P.oM<\@i)i`Wfu=Y1qVFcc9JNMGt^6W)6CWRSDK %r)#J7M$:/<SshWLH#1NXcXskRok4ZP'85P4%d6b,ojt=#HfDK0=sAe]eZP'k\TK)G!>%5@lWd?)BIh+WP*A<V&HQ2>H8U@_TZIsP %K*moQ$G$J#r#Z?J-AD/o%VQJO.<W=BQ,2L%9[(V/7BD83cYT1AHr>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(h<QVQ%VTq3-6 %V<B0m52]/)6taMRjlej9eHAP3Z06h<</MO9U^FA(/dm;Ah(g93WhiNOK;.VC<a]&GJ>Pe=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\<oNe=c4Rfa %kKpXCRgXH_2"oaX0j4gmnAja?`Yl(#@]`0a`:[`##.)T%A6qWdCe:W<-EdbJ0kadI\YM1+6#KC[EAJ\_k/ZJS6dX"EN)pjJ?@A,n %'J,/?Ne)*;:73s$5_REn;%lo*?d%1'1aIlBU?\O3XJ]#Xin@U;&S,X>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<N<*9<Z6r0.T]=4;+=EXiHWX65"=>!Wm&K*i(Qa@b:j,(lgM(^7)Ns]lXE8M)!&b)epsjO %P6D_9.>g(tL$N!:aVp-&+=i2j?k@<bp])TP<=0^f?0J7*oeP-IM)d(J33,4ic>"jRE"Z3/oF\*S-I#%D%5NH[#SJZ/=EuZYlB=1Y %ej7cr`'qc6=<D'"h?n9.,AA<So5nj8cA#2?&_d3;_&S@"ii"dect/^Kh?qa3eB8nD%JWg58A`$DjE6[(TPI)e4]VN^`^AIW@8`dP %"?H#Lg,d[E%AM!L8En6R'kX!%\JLH.'XiqY-&VUo(8F_XAG(X&9mA=(.L;%*W,cKc>:'sWZraM'i(]CI^mrH&jNR@?$#7aqmJ+mF %@!ud`<?R$$*^YC%oR_rpXmJG)Dq9GXVt'E)'bH?^Z-i6sR2,rqd7BQBR9=Bgq!"sp"nZDbl*?*K/AbuJSu4T!kKbIeg2frXTh*P[ %_'I/DOq&fV>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 %!<g7Na"g[H$<N[4JsV(S5)]O#l55@hMWUdsiN/+4980]i7-,f8^-7\L?TJ\$p!E5,*<8saBn8*-qXUS<1lXr"8%#<PaS$6,[)u7M %c;R_2[7`0OanP*ZLfTfnQm0o-M2<[D`m4lqp;W+O*@*d>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<Q#e=U7fX7@@IFVFK!b4`GKu,&9g:M!B1#4^,2Ij^aq-@],A(TY$S'3<[MVP'TfFg*4ThkmH(9.D&m#K.A090ikC>>Y0)S %eRSRU+R4T*%gQMH$>D"bd4r^*,cs3cn9!0\j:e@5$`\5Ke1*^QjYA7/WTPQB?>974Q4E+uQ=kRdTn+Ca70V98PY!qZQutq&K5=UC %A#<aIlh+p$_B4p:@p3iYl]\:1<Vc[+SBi\,dlrBiZ:(;G!_adTb1B.&_kcg(H8C^4P8.V>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'!sY<j=`-,PD:-EI]lE58,n];U^-YH^fRKcsaXep*97#8/r%N19kGj;JWC>m]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<R:bQZO%WHu^?$X!pPV23//o/nS+\lh$_dhNRV"GmkCY4`H\us_`P%pSKVQ/eE_Rs]7 %-nJNj+4Q!R:QbZaY''%W=R3N*<h87,#L@NL_@bVga<lBraH_as36[db]%D4>'Dru.2X03$QSpDNe",3gLP+C9\;S6EgUN/o>hccD %_joKBFrfZ##"!fiK:@tjK3[2jMg`1#S$<T%GB:)\n#;=bGe3WSh;f(O3P3CQ3Q0__#O^gdDt\iIb2l@3GC\-]<p]mr2qcW3*8fgb %*T[&$$1e/[2j"sgEds9**pWe2+nYgO3:>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)<fEkK(Ll3;+l9-LVP7X>RB#e34j_-%$mNZ$)UmK+_gI^_O]%]gBV`W\Aom;Y@S[:Pukmf2M"5N<<L3^Aj*A6j_Y'7C,:NsO: %`B;6?%d3E`#<^E4*9;\V(#8mcGbW,;a:1q0OTTEMG<+^**tE>3K0eMtLHOh\&=5LAVGDEYK-'h`a#s:Y!7s>q3t?;VP:d>bc]]C[ %ER9mj%W]r1G5WO24`k[oS"=_AOJ<jl`<!,fG3\L9f>A]%m]\(&4NM)0\i@K>:HEa3gR^"CP9'DOe\8qH:]><hqk!G!O9;*\_$@O? %M(!h7';'AdJ/b,^`$(.g\5H+Fjb%=1l-=Z9D?m]C8J_UloLTipG3P3_+6:paO3Q/0qQLG73;2%U^!!qsZ[B?2+5W,-48>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:<h;tbSD]cIdGETN)Na-bB^4(ESqR`6On%QL"ZIrq9),=pZ`A$QV]jK4!8t9#J4Qhi#\l#f\7+D8lS;ULM %,!EHS^LpFqPKcXO&.tlJA3,bo[=PjgJshf90JL7,?reLj,l>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<j]abhoWs^<#Y=iIEd&7o<*GP2>=YMHZQTcAR8t %%4s^[RhaSL/0pE(QX&$a%k"L^=hf`*YAo+p0H75qN/YVb/5?-`@@JRtg5II7djgmQ/36`67<_tP6!`SC3`'_tnmOm$)"L7'QE4pH %a<o:E1,pk,g]JeW%Wno`$fFUS:r,^Eo3#>q'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<QEX?@;RI2[OZj,NQ`.<"G^?1Aks/\uYVhZq,`)=ANRD[HT=(2U& %3L`F2'p^P3D_^e*FPBu,TR_Zb*o[k)+MJR?^'U`60DQ@3[?;@C;Z$cR#Na!+neGM+H2.b?0rUF'S"Jg:d39,K]R']Tl6!lH!EJZo %@>_]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<R.BXb]3o%=CQ_+"94HeW0Fg3Z:[$42MFU^MA@mr\@Qt1WZ'=r3`87Z1/DV(1uWde.S.c> %!`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<OY-%XRJ/\q,R*^`8%?`a!<4Q]+]m0!skJfCWjO<$jr38#4i!"0^FkIFgN9-p#(dmb"5#ZW9!IkRZ*?Ih3R06 %3hKfd(",LSHGU>_`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-<Po!5PTm:R\ %ooq^^M]!-%:<-0JJODO,d2Gj*ME(+2hG-GH(0K*bP/gQ8IarY,"X\h6Z'5j!LHDJ<?bg)^eP%/=2%kWi%EM(SZ3Q,3i^BkD'ANm0 %dViZDLkN%(5-IC;NDc(c09<_!jeVTP8AI5!+,)/E7mkD>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>3UC<D]SV5Vt!LGfqeb4S'FPUIsS`T@kcU.0RqtGY5pO+sgU<U4m&Y>US3m29Ct(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)e<Y4d4_SXe?!hSr8G"*%QJa/er#-km^'+p".k(fThNJV]Yj(NGJpal26s84J'CQSk %*(8`%TECk^0`DcsOF`ko0MA!MlkGRO;0591[kkIo0-Yqo&XecJ/1ttp`?AXupt.9(9SGN(N+S]V34.)I06+757@<%Q5,D,Aan6@4 %IT#ju[;<(CA.!^rl00h#d4/VLB9!$ZKBs/ZW2XW=AuN7R4.!q&BC%=fek5K4T#I3@ZT&1Kp(MbXqOi1;6+4oK>AGH[>GZX-cc?0P %e0;iJ:(`0ofg/R?R)F>X5[j!2h;&_)8)dL*#/KfT&4dS[X:]Ymr&A2_+?Pd#+S3tN&@(CT_i(b!o<d^6YX3&cTGFj%Q)K9LZVTAm %A=iJe*JK*>K&R>5R_#T#T1Rkm=:!B+^#)`K3-cSWT>X/ekUI+=L7gjXbVPE-377b"Un_BHk)g@c_PjH^"sVhqKe&3H$&hAt?eg!P %i].0oUYJ5q'@;;]FZ2.'<C*j^$UsHagGX!bqUgF/"BS%!OoqKo>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)'+,F<pilO@MZgI`3b,C]>I9umL,*!"aAanI&)LpEPI?m.nUp_P;4COUl>i4eq.T>j*rG[;.G %`i`\>1<Jho^g:\5CU`T%?+?#a_GL*B183Sj-j\1<Ib*"'bk1S!-?X)[K>[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_G9q<bKF %6*NU$2_6`f1c5[*%p=I-Y#EDV\J\)EkLPPtUUWKZK35"T?C2W#ZfA(Q+MrIaWkf#6m8I:`4S0]Di@BZ1hpq;#*q/s6%D=&Lkshgf %q!QaL%3&a&6eKD@jJuqFp5KcY6Ybes_:+XBUZ<B*#19E%k9k]4![F#Ce]lb,c7Z(p8R1-F(BnbBWt*(2W3p;];>Q>:!2dRaItMek %q]g&f)_6R(rUiOmh1=u[$"Gn**AlD<lTq*pmQ(hc5u=[JIgUXSZ6"X6gH\ut#'O^P>/+34c1M/po3>4P)L/t7XnMVo^#HU7qjc*' %/s+7m0Eps:+F6c!X.QRJG,<N+?I;7"5s(an'$$c`;_$qI,P7#e"c4t]pDP>``/6HnNfGlcY//IOSH)EZfNp_(6PQS/Iig%8l1DH) %''hA*)>\SuP+hsIcEWB-2R\pX=P%`pHg3"O[X:i06M[-69X*XAUs^?[HSbkqkIc89juVt*VYVa#/RM_Rf8nlY!\=_"lkY]A:qC<u %'7G)8K.3aUd[k^bF+e4+('G1$l;jRjRf'mkGuiOCVT?/^>ce*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`[&<NW..H7`s8\QN#^.6UK %;+#jp3,t1?KJGFH)l:=e@0[(JRPW!sQ>*2qN%0PoW:<9Gi$+JAM>#DnW[9"_Mn%`FRW+7kl;>8Z+jAN:*]Z,F+A2_JO=Pp!!2TOS %T@<b2e?Y1d?GLi[=Na838n=6&>$;*]b1d2c$\<:0Kk_<",2b;76<.%&]9>)liZ)#A;baNIK=I/=-$PFeAE.X/Ar_RSo9L=]#;.=3 %.A1%u.\Eta&aLE`rGr_9U8W/k<LE!BZ_kr%=?mAi^E7!_Z?20>-^.$0>]\i`^cj%cN:?<(4,@?MDBG'W=Hc/*pL>&jfZt>-TQ-1\ %U<n#OdP&.9Z`tAE+K>,$P"R3pgE)5<'FcI)^=AH7-[hGX4*I@%oe<I@BiYE"$EVi23tNFffbWlOG4=g7=lal2jAV75Tu4%`.9h*f %qPT]dlCU)J89FPuN71sCT/@u0TeJ5^o70p<j6(IKG:./?(8B)`J077@)*^Bj##HUd(+9G`SAY^^2a8\qY"T&b_uk5n_Wcsh`%j@5 %_X:qD(H%@9d+KC$fLhPoe2BjVSq-/lh`u8`@u0iZ<WfVg3(nu.-ZNcf#HdA;R3B3pAJE>@41.muOF_?)^e"S0f-!:60QF&MK7#,) %+*bN*]ML/Xg7HN-!`NM<VJ#%YR=aM3@[+s,e]&0H1F?766VRR^L(oTKQIuu)Ru+55X[lM3i13,&blQlN%2(-=6-s?X8eh%:/-hEf %!]#B]#T@#G,--@LoW\VZlPRb:`16_`cU2Ydf`[Hu*lI\^^'#Jk[tWY\9GN[M`1+-.q6n9H!BLogS)KLm#*9s#oq4?poUrb*\;?Je %f%RQeV2-?q\([ZL;TE(X2oMMFLplU883+Q:;pi"C`@//VEB=Z7L[<'3ME&><R8"n,Ajh3\ZUT5Y1dn_l98]>jTiG01Y\J@:'e#!. %Qo^<r5'D6X;A_5M9prD4NB7Pl6=o4Z,Qhur#'"qV80OgNY_T[7-ORL_?D3M6]>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<:pX<m.==l:2EV<3Tp6c$1-bAZt/QOY,5XQ %&D>PgI9Y5;8^"3Ha<We$bdJK`m.=:g>@(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'g<k(&)P&g&@eqqQCg$CJFqi"iT#1("`j,raR$JXjPl%:Tna>d(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$<r@gA,%[`^\e5_**fpG<3Dp:uhI:(RF,R %g*q)tpnVS<S0e\*T!8SBo-?@Tn<\+TT1g?YfQg(^&S<_q.q_%Z(/tA(=_+lKboP8cX<L":/*?kJ,rmNFTuRU86-/>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<VSkOD8j*q@02j=m1#IR%<Tl %_4$"c#>=AJD`GP?HK\,k;W9CW2)_>ZN\""!RBl1[]S:^67=]grf/(W*4*V3<$G<6ARugpgLHB)(^(9kM^\!E#oX`4-]s(QZgX<p/ %hVEDPJ`#iMI^aCoSR1OSGcWj#p)TT>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.\7<d>3I736JJ@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"'+?@'[<p"F5"Jcht74!^iG]78D+D1q_6!0mC0UP*jXG8&9GD7Kr8&lA7-8$YND[Sl:)X?Ud^MBl'Kn*'sb2*h=n6(sdG4,r] %C(aN?`n4/[!o+adKaufEfR\8JpO*gq!Fi50J;]C0j7.(\TpG$H;$QTAQp[FL\j!K,dj`%2=RFC_-&<"pN4,$ob(DU4*_o$CcD4uQ %8BMtV8b^gi%kDh-PY$j9K:6j@IIa$CFeiVPJ]c-Vc[uu@j5A9MCG,Al/-:q8E)'JX/dETANW]Y#2gZ+.;=Ju+o`\[Y'G/@='?)#i %r1*pIHkF7LecBgO0Sj,[;8r.X!C5mWe1u3s6#9&GeZt]M)thF,A5[!#"70n'/130>-<c"Sm+e*KYZk'Ka3/&W,QP.RdfIp*UEuU$ %eqc5:D?RmYN=(;\V>s<OYrf0\r^MG$-YjQuLa`d;(psGr3B*s)&2D?F!6/$RWd.7lTIc8:+/rHFi]FL?C;dn/29NLgF_rAj^CRkm %1tkOU`@!oeQi2u94d4r=a_sG*UFM/cj)V1L/MGD!>>e1(]N_LE1PjJYdL?'aS<2\3+:`o9-K@(+I1J:;J(2^-aBm=V<@L]J0Yp/q %`2FDF)-%qNM$OS!(<LK\X:Xt$#hFrtWQgSCLa%A5%*:+%+>4/Og'\]lZ150.eG<PEge3lc?TtmpU4WYGTN*&?WfA,?>)F0XCLUC_ %:8Epg0NsK:QAK@*$%SCm%T+`#fj.8AWq7O?>X\X:g4<jr@@sq.*@M_/5oW*&C=&h#*@,1Lp)imC@4.tQ'<5eDg&(\X%C;mYh5^W^ %Y7.--^q&9E9-@1n!c`qY^LRP5Rm(N7+>2@^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$-<RKS*E8K$ZmLe!$-%TrP!mj$2usZUm:Be %FepFk.hF"tkMflt7RV>MRU<VcA:-gga/M)N'ZWJ=+3YX>^+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';78&#01EaH4Y: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^@+<fo^DMM2,;\Sb]^bMO%iO@bd4n#jZ-=*fB&NLfqDmsSYi"D(KhfeP*Fc]Q5"<U]9>*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<S(_RM.#8Zq14V^/=,Q1C+QZnC,(I3>`0:'K#b7H``RfgXqj=[*qd)H<gpF@-M' %<1AP4FL'L0(JCVb\J37.4<g=?q8l/b2034mhU[sHfqV`<[K!c>27jtrF>-5[bKP6$G&PXFfQVjAFW+jV!hS#T!s>$19RC'G#J7DE %SW;HWlrJfMTGqrFfG'Q%lH"rmBW4=F(qLlgN-'4a>?N`KRrYLm#G'<TA`Zl='W85eW$r/D+EX7]"qb-]1f]NfCO^Moi^>>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<UOoei(re# %=dir!GSY6W%98D"A$$B$L[;?O5JW_U$ILk237,8LG&U@6S)[\AB4&\'QSLBuG3.I++<m0X'QLa;8I(=4@!M*%%'h2qEeY80l59@\ %OlD>=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^a<D7NGTAm8Sep%dN6e-$dh3'dgokd_O %*>g)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<u>?VNS3M\BQ.6.K\,p\Q<d6rEr`*N\5Mk6q2`.h7G^!i?]`,Zc5,G\/@>mVFkg.aX\6N" %S>Q'(3X`2#RnuK*=dS<nVnhTOZE[6s7g5r@dZSsfr)#*(ed2;N0"Dc7</c+Q[Ul:MfqUUTCnj19_Zb]]=o:.5BF\\3oi\Y,L]q@! %L+O9?%kdFJl[Xjt,IJ;]&M#iANQ4c0V[RW,Zom^/9'JYa+[M&[-pAnG7PId6Y@Z7mo(0b^<kZd^H?#2nc,6st*d#(>Gf?(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"KD<Sbp_B<dJllJ&Wdlq@jDZ;/&\_dE#%_><u^kd$uhAkr>i&);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#ar<jPsLA;%F!P_"1EKc %B=ju?lpdYNR82#T*aQk71'.tjXud.42aPt--82a$"1>H3[VZ-fW/1tqTVq\@2ULGP4if2Aq8mqjf%.\iS_B__2XJoc<N1;9]f0EX %eo>%+m#pK+%[bdnhX`q&/[RWtT%V=cXG0$AE]LTsME^O](bHgRls$b:nGHhQ(=lU.lF@C`/Hifs^/E5@:i>u71RL/I<iq5BTYZrF %Y-Gtu[`@phhRq>4n1n*4%2VdpN2J8s(bO-$K4.F<5c?k-YZY^FdR)jB&f*H<FN]ZJmQfkLke>Aj:q&6%3`+AndQpM8f<k!!l+oNU %jfNKY&=QA7FkTLjme:JH5R0kTMCf:1lW(di[4N"B1^f,9b\L5o#ISd]Q\Ie9,H_b6(']>OM%544W<6@>6.`:iV>:L?+GR(/0a<C) %%W)P*&OWn^\ss9D1ppQd^4(27o4kMtBXS<Nh?Dc!;<g#bhJ[4J<NBDWb8-ta00>@'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!&<Qh %eR_'h8UO\k7JHCSUV,E#;KZ-B\IQnng2Cb1)$<Id2_8rCFma)3Ou>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+p<sQ/G0R1=#K')MaF(pG89pV)4Fd8*<_j4I9\V[E5$F5gdW\@L? %VW@r%F_Nt:3U_,=CH/9MNr)o`NbP(*LuZlj9<d$Fi:;Z3QEJ;ls-QIVk<TqKgfUsRdWu6QnK:`9qJ:sY!f*k6noN"+MP=Hj/e9"S %=Yo]ZP^Or@-64A2%a8lm4_2_HLh^U95!L1\P"GtFcHml/N)\>PSm5IHZ^0R48mb>L>DqR-N]M5b43f/:['GA7E6+tDak6u2#?t-/ %9ClEIA<e$<;UWN61EsqQQhutFP+M,bi7bUL>d%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`-M<an.:CCF-:+?r,.9%>PDCm.^kILRuP"-0HBdfK!a6oK`NlUS`ZG_fKs3PUu$X((Dm87\l)I1RHQG[U>qiL22d"IRI>L7nG %UW+g5a7=O$<TfcGJk%.oggoFQ4N#nj"(c"ibU!Ge@*;m-p]>!`I,8pA?>+"GUIlOI(BaJDc,dq+<MW2!V"c#S&?[;$o."MSo@oIJ %qAV:885UPaj(?<5E+";#_/iREG6K:hnBd>`O;CTN3$_PoSu`Li7)a"#3=e.8ZK6s.(<o5nQ3EdLQo:b9[EBR`.etXan;)*YbpYh" %4%2O6?=/g?LOW3=!8333JoddHRS(</Br8Ug`TWci^[!akY[^anXr.f;WliAJPXBrPo;"+.7P)j8UfbC.S.j=#OT)R;Rd:iGQSbPO %akY<TDMm2?paI:g>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&Wer<e\&p.oPjF@97LRA"4:o9!<jrG[D]Wpb6[cSZB)g\`8l!R[4[<3kV;l_"r$K[JomZ(kqnuKDjnIM %l;[ljFY%\l:alMf1`-`pf7=%/hd-ZS?oQdE^tEN*<0f9)o4"N#ZDW'dq/M7Qgf<7@,gdfehX[Ee_WYOa9bqmU*1B&9/(U(XK)$`c %\#l;4CQ=^6KL/CQ!_F.TTYW6pWp2coWE6JQBSl2]K].'WVOD>7C1W3dp;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@RM9<J7JQ>p+;+]i#RKBlK>Hh3M!V(>1=*W1k %TS!GEXbP7++>X#+#0I(Z!]9Qi`1Y<Upb=]20*BRI76EaO>$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;@8ucn<HusO]Ake^BF(M.[V\Fkt&jBdK(J%'a`;;B\=,d`%cdC`O]SYG=n`! %mQg@kd&5l^1XO:?bYXVQ.3,G5*W%U9F^JqgI)0Sgih1tS2biMHX-f+U'kFGb9\[CV>0KFY/_#hq-1W`noc<cU=#&IC<3A'@4c8uc %9Wd\fB91J9hs&lJXZ/`g_,ALWe!A!&`b`>:#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<k(Y#c8O]]@"Z9-/HP]/93-X[J(h3piAetX'9=K<?LcV1!>_6V,<L)Ke&8=[c"7A^73r?a"-%(bpc %AGg+-0aBYY\$?b9'V?)?L:6EIiAe2r7n*sV"(NjI975rr(#_h9%I.Q<!l[m/Y6[Y`')&9q[G-Kl&d?'A@?q!4,Otd!$bE0+r<dCU %"Y;]dFu3*]B]R\jP2A.1>+MXKI!EN,*Pcij,.'>:[<(rj)5M?kD'FjL9JG0udhWb;SqsFWkhpUr3Y;&7c.1]_;NPV_\0CnWZ<U$* %/Ml-C791c<Qc<:1'f33`B067FWLHO&)?d'mmu*g3%8/sj"`@?Hf'.*nMXJn:31O%(Cg@F=IGhoif@3HhSrKs,U9kLY!)C*V<\5al %\A%ZokC>\=o:l,!QR7SOLjVl<AKFmgrGEO3W%%N[7fD*FSZnk8DHP^5BW?CS==`3ue.>i>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;":d<TY^d %6bbjs9J?m9kZ&ju/N/8JQ$dr*J"O]qraSg3Wj7GVFTolJ[@f!d9:\o<=^<dP;gf9<#r*MR<hYqj](2N=QKHsm3Y80<.S182q?&Q' %cXQ$aIF/OH*Aikk/^]-fkcEJrd.ODn_gV`#jE#ofZT\$?U?l(8!M];!O50nifi`IE@'CqfI8*R%H]kDAX9oZA&At4l6$qHEW.E3u %aMR_&Sa4UW&6/a=q6`^K'2KCNZYrb(o[mVkG'56U8pIlO,D!C()at.dEo?$D6a/9lWtlV-l%g'ZB%>G#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*#3NR9<c"&4EUS!1&0oDmj<\1k<\26V;N4;OP/N_f)2p871m@ic:&-ciQV:]+DkMWH@rHircY`??h1Y1jjW9An %X`Q0p6'tKfQkm)sj(#P!$bQX0[SW*;j'gWlYdC_pWNDoo/DCX_s$KMUWH]fHm)gGc2R`Pj\9"L$I3NjC&OCm")_j1Z9>H]!'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_"PLn<hi\%nt%OY=Epe %W#73B2f)V.l^q#XOTC6bS$#ROHUR!e)]>k<0^3^re9_`c/dB3U@:4@eZmBKEd6V#&WCJ`gMkER:>F<t1gUs<`MtnmW=WYhI;3bd1 %,stu.EAYOi6UUj1V&B,!Fl"I=X3p4O$Bl!4$"&[bh1(^V?p_fYR*^bfW`B$m@8nb279B[eGLX6&o'6M/Kr05Ao@F7p$Aq66:Wa(b %&,S!>pNZ.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<J;i_A+:<+Iu%c\6b!s3DQa>: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<CAlNIV!aGu!q9^&JhYcTrfuP"GnQ6'?IKV2T#T2=BU$`FCAsI`OJkD;g2<(:<rU3,Der<\e)*b1<oUH7,XYG'6 %e5#Dr"NG9T/HILJ1fgU6j7Z]Gd\pV%gdCh&hiSe)\ef?P[21u:F[Ik7JjCnK&nnP=K=LnPbFo5`ij-,i'&i60/G%`oGo`4ZHW"qZ %98/]<%hJ0dK,2[@)g\7kGnq`VER@'?'bfSOTU'1CL8*Ro>;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<S["BU[K2IXc %k_-Ou;c8)Sas,"p*&MEX*&*Rk6n#d3"ajDSGYAMnA0!(ubi^Y[8GMgP"N1dS!F+!VCQpPGD3KEU8iIEN,>: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_:\+^J4<kk1ggZOC$XJE?JFR*kQhH`6"-+N,.S3XUF[efH3Tp?0NCo&AY/A-n>VP9tsm2"Mm`V2Seb8HD,GmF*H5IWE: %c<Is3PXd9mFPpT3mB2'Oe?Y-`#?K7.\^;5dEtmed%D=3K5(3?d=AaFGZDm&U0CjQ>-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_d<s!\4Z>M*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^m9<K1V:%]5`Sf,<2+*Kfh$#.5tiXD!hcd"EG7[?=c<Bl^b+NSNX`H"Or&Sh]c7TNst6 %<$L9:.g]ie.Ti%!qgt@JC_t,\m?4+hPb)oM"]C&$-Z*Wm,)AfR0^@g&[3B2R/;"R#j<Ys',bH2nrto_0k6`Gu.Z5e:biPR2_Q!Wu %%eSG9/9eS>4\>$+8J>u;bIMdMW;RA*5L-#PH@uJ$':/M8R!_%YD(X2ddtcAGg<r:9SQ_?NeaT&PP^Dk\FLulggRRmN4[3)er/UJL %m6EX:FU8ao9j1(hERF-U<-_AXCG%&f)ci:nU'SLk9D0q8(`@cmp%P^Jf$/!3@i?g'&\h"s2H@aZ-d1q?c##"hl]<qKE\;,E`G(Le %E)b(,--"P-9aeW^LI+Q9`5-7t3@=?@0o\8knVC?>"-!n"QW3hp\C`n[OZ/1\l-7P;'sh@>d!$k<$\qmelseKAPg#E<npSEpE0.EU %JRjOL:5<8LJa")4F6F4s.Ik/WQ:Xdc1\_5K-UR6Ja9LLAN49j8/g\XAf.5$bINN013:q!+)^e"0)FAMN$UQu6KWcWjahiCWhe14* %1mic8c=0OZd"mG4g+F`!3Dd0Z60E!^(*io<*R!/81.ZAfpSsb-\R9-^B-t*:1EoA1$Q37^1I\jMF+Bf4Lp"66DLkG,:7\8METu&c %;rb[UJ]d+:)usL\$^QZ.lYdqlE7'kHQ\d5pi8a=;+3\Gk-9ogkWImu6kCD;\"mmY=<_j`_mfD+@(8I3/#X!F/SZs_-A:@dbK*Gp\ %c^Rf)ME=WL70Q19oQ_Nf<:_e?3^?#[[d`5/\'=2'N=/2\k;iZ=Vuo9HFg"?1S&s;>n(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]#,<m. %";f64;@jaX0og+Q1c.f/&<OnbhBP[<L:<5.27<[O-,"ek.Jg@Rc2Z]O\.)sAd6?:4dRQ-s_MV^q&>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<Bh#l<p(3D-2D_D`8[Ubna##\)H%Pb6i/Jb85dd(!@PtUT/,bq1*7e1>>HVCq: %,]4&lXUrrq(FVl].o4qEGGCIf1kDs'BL_@PMt<ULZ7O<I(aFhoaTFQ,&d?fK%nQXs;F93.Qko))68_S!_/`WGZie]4o>f?$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.(Zal5l<e`VM[eCHE`[?l?W,j#,!-IHEmn#EoHS\j*MO$TXB5dE&*MF[#EI`\& %gVUAk:,97)K250a#D>6HrOB[eH9IYh,C1%l#N?-/7S4mD=r!1,_k?)_*5h@#/B&S0OnJH>q:\H@/k^8aoAJ-8egrQF1,hB!LPNQg %@dHmo=atYOrgDr1'tbD3(m%t[%.YZe3<G<ldT`BtRrs<sX'Hj^WpG$6=2U]:*.^i3P+]p,H[CZh=`ROsW2ds6/;jeD>KrfH5:hFu %?WT.[:!ogA^Gr?"Yrh4/0#DL'X%qjC#@Bo,%td>Jd!!/N9Ej1?!,V:-aX/=pghjD<%7*UQ02no3QdeXM?YXCS7TCB_mR><Q$hpd. %q(u=G0=XYa"O1B')#5ScWHqWJU'o47h0W`l34()(NHLkAW>"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%XJEW<om`[<. %LRf4WI@Cnc]ZQMVp+(;i"treLJ"*Gai<7BiR__SD/L;_iGuuE<SY"=GS!8Q,f:i`tF1#JJaKf\aN\P8R<\CamWY8_oWmuMd7Ws#7 %U_s7j6`O`[iXruf`1/7P%LP$">mGKcQj`*).Vmml_<T1*1FiiH#+p`>^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<aUZ# %EI!@13s<Z\O.^0aQDX"[4bB@bGPdP,')m&ZaCK1N,"WO11g79s',_*B414@<mIptU:=cZ"a0[V(^5f'*%n+Z:$p`;c"M<q#ZQ>(i %Xr=3J1I&I1*A+/8._!jP9<U@]6DCoBH.8JT.96b6lE9Ef\nd9:;u4%hV6-dC,(>R%eg.-$/ILo@eo5M[+E.s%*l2+4bGlf&$8ra4 %_PD'5g_:!odEs*11AeaInh_u;aLBt4/[9p5LF*h]WpEEuZ*,#_&:C2,<iT6;h8pSD4Gl_D&Z@tY$oNd%VnQ,KKBHhhcnZXNg9>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'[%S<l1$%R4JT7aOl`WWC0f'I6mtg[$^Lk`Oh$g!'*mU %AKrn]W[uA%GH!/\c`Gb[\N#_Ll8e4HH>6[&!>PYXjTQ`B?>LTeoK)S2"*=HHB$c`NVJK!^,P4A[+d,@4PX+B"5<r2'.BW_7R)_r7 %s&[R=1["\_P=0G#@VmK8@#eV,ri_grCP$2`3l'/LXS+UCbZlHNM)(6<=TBmNW.klr%RF.(\kQU4(gcap;rVRq>7*5<DLDH`;t_h* %[N*JRfGE%%E2(GX2O3`d!"FTrBXF[3k`,e>J-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`<<Y]qYhk&_)C/dXS6#L8+fE\j#hIM7rC$B'HhNkaLc+pPsfWJrGPT'p&3YoL:;slsJP251DY>#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+[s<?C)s;3/R.Nts@.6oN7ZEFGE9m80m4pQpqhU1))Zru'4bmFb7a,ZeS*%m(?.kK[:*P&g.d!.;i69-J5P'buJaEYRIT_5+ %O@R:174k&i$^2$BO+Q0mkh6,tGih+Fq>29qZ9;]-OK!40`DTs^)L7t_=r[YYDf&Y#dU5#Mr<++JHM30QKS25L*62=7*Qdhdi0!(: %-5O2IS5GW]^P,D"ioq<<acL6>\'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*V<c[0j`NXjFs3IM3p<ef^=\@"]d+N0*r:GdaESXY'BDa_.fo32\>4r%#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<Yrhkmnt"CnZR\WKq_h$7/b<LOnf8)4#'<X'B<PMO$6Ftkl)9;=35mrA!!Y6a^MJ@tfR,F" %i\La^&2AVr387W[mHC1+"@M4)pQ0B^UJ@gm93=\XPN+Q'>%)eSm[2XI>s>XG.DkL_Nl!+kg9R0R9Z9C)k)>e7m58!:grB#]fIp,7 %ppaJhp09HQ*=Ps!(=lNs7EF[5a=N7Q<(mEPc_fL<H/i(dZ.F*J*0ulf;A3mTes+)dH"OFo4#&,?k&-bc\-La16P)ZJgZBb7rAjT- %/U^TA"UMCHS(UV%mDt^5bR&2'2>E!hFGC^mafPeh]`69a1]*K9hlZ/'b+/e9Wk)`L?#MHCZPK0@kE%Tn9rWGa[kE&=;'<YLPDqcn %;B*nK*3f(CT-ma^h6.6!nR>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<H6EHtu9sOj7E%k7L)+)<>@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@O<V20RC*aYM0Q-Yi/6Z %U7[eliZ`Fs:W>UDLqSuV`U'm%&^/Z#$tE!7<g&jr&(KoA)O+DCQMgIsB0QELcA&QP_$5JfN8Wm>#h(f\^l>eF!t=u3nr,U@)ghQj %RZa:D&Q^tjRs5Y4?(0=Yp[5*;6T0Q@5fEe2ECZ8hF*aPLEE8XAL80WeJ^^uHHfojU^TY1k;Q5i`Gc'Fj)IDrPr2^<d]s@^.%9;uE %ntH/=c6C8P#/iBd"LSG[XTo;2Y?Oa0:UcfjGg7`s^`o!eCSWYk+mb$:bVa8\7Zk=G4mC`f*(=&qj;5rK%(>T9G-I1<cQ/ejIu0-1 %.g[gWi!d6-;Ykrs!!2Y)?\2:jO5\#=PXYRm%AdBK[$rmX'W="nDE+'qJ^N5.Y.=:'#.f?@6eA:'isp&Nl11"_mWsS>qKj<("%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;$`/*0TralX<G;j2M;L@&#n=(IQF5ki+ %:huq65"Sm(3"'C:#9/6Aj+6sR]F>NWY*-C`HZ3T@FHam)/g7*S:m9nM5tgnO*&*Nq-Hn<3E)pL">5^qsGS\%Orrbrsa+\_Oqu+c# %6/7?OG6f<3*[pC<`SZ[a<C"fu\X^-#91#X73m(<UH($Ng`OsqaGpW'iP,e_h`BXOPV+bsaLob=[9R(m?_;E96mfSpn,E(p!B=)7* %qm#56>)EkB8Q[=$MD3J/*)XRZYs]=*][iOj2"0Of*`)i8+f@m2$?2Uida2uZcd6><r.G'QR^JNACcE3]f0WGGnQ`:6(ONA1DfL]H %23<^<$u9S\8!Zq)DU)]D\:^jG;Kb0\/Qm]]D_'Q?j@+>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:oh<R=mIOh@C".,5DX\EZ))b@!;?XT>2KoHj<i&@A/*!Xr%lJ"o %op^Hhc2R73V,#gWRI4TucXG&":Hu\NH7q(\L5jd*Xp]^\Ak<QgNpWBU.mOd`=QYV?We,TmipAliN#76YU<qD,[>oiXR,B=pq"k!( %/M:eOj[Rs%K?<W1g9LqgG8b<)nFL.CO]hF>i:%)TXtHns0Hp<Tn0I[BIl!n7hd3hY*oCb"@U$!o:dDFBNmbQ4#2CE;FW?$n\skmb %JL?>2DrKU[!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<IrZEtHhm(XZN\QHN]26@=H %Jf36b"HNZ,,QE>'.<Vn+56YBt]^5afHjXa_=JK=af\R#=o2rnTA)+A)?Uo=!T42R=U)D#b_#Rhl05V(")d<OpH<+paZ?h7[[F3C( %TEnr2H\)qC?A4WugSWQO6,+jrI>mY8'p/s\/ljYBE!<R(5k]`t6Jb]WYXFM$J703'NYeXc\(<ZK?$*$E[r^,aMOVbp'irtn&4,U] %7[8BtTO`VS?Nj+Sn7:H6+#)Jp63%0oabilWYm)sRdS)/!m07]e0pBkE`Xd$&2NW-U#.>ScD9MZg;QC%Oh4fFMSN%VgIP6>$_5>-i %KQ3TbgEJ4:_"B)k$]/"O[j5go?rW2Q-VSAY(qM+<o_4#cgH.DqNCZ6"$OBW11@b=JlS>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!%#<eA_7 %+dh3sg7Ds=)]tZ'/mt*g8'p"bC6XE3KTk<;S>6TTKAZJ<ek]0k,o_Ac1Eq;O<2iWIo_HnnMm^"-!DBFDisoS@G7^62Q^XDC[0<Bn %mD]LiM!-_?,E<BUJ$(@X.X@?$!-5W"iTEMl/dqYb(++TO_$B3(>$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"<?+Rm:h?W-nLQ95igKn;1mrJKefC/rI>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!<XcY\.%Up<_ %o$]G"UoK\1#<],$,=jncJ?[+CI>mYZ/]2P0A6Z(UeI?p^>\3+4:]r26%+RDGdb4K(!<;i+[Y*AE4o"lchXY(ajIB/dBPU8aC2;!I %</_Qd3XQO>'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<omHA)S-[jJ^N%c4RDFdJe*7s(?FY/(W %:ej"gfo%D=qJ"G\.+@3#n#?k,3?(C:.S=lt/`GQgGMrCh<L1!Lm(Qe/DS#Z\Zg%p'd+JL^7Y`%@^[sUl*jt6u:`[`7jVRe(PTJo+ %Z,.?uEXt;]T5f8_[`hg/W6;3BiNqi\&Ii%S-IuLn_$\GF)2`97J[L;HEh*:Y;\g^d)Rphc[q7&rB7o5fmgeoER>_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`T2<oCETLW(B!YC:;kP@f>Q4Ea\3ueHX]@;]@Ome@HQZ<!IQt1$VE#*_`L@iR).OI[h+:)`c/#Z"qX<O%m1CCW\ %V!np3<D%h\:b>]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=<Xls^fllSKsacAqnP0E[jH4-<UNV\F=k\Zs"!67ZTP#8E4>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=jMpB.$rq?[g %Ci'J:W<K^OPQ7M=h"Em,IMrQ<^`^#1Q7bGV2?>@(p'k.$d<r9HP\b"WojH<.m/_A_$CIU.Og_[9>-@?@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')ESnt2O4Aj59ojc<K*^2>Y+.eIGsjiY#Ar2r"bt*NTe[ZW]TK]='pT4rcF$*MZV6C7d!DfG'/51ZgFiO3W!%Bn %bup)t\SDF0Eps.#!?MZbD;^:.<eoi?r-elE!u?"d`fhiGSH_P_NtNO<<9t#opaKkuU+JWgUkcqE<.3,uSoLff=&9`:J-oJ1c1MF8 %9HuARMCf08X%3&YT#t:0]*:GXCjZAOG>L+pOt>6&Q(<M:H2.1bHXqcFHL78,K;`o?,AT[dPE7[iC(*:HDP44tc[u$\gGp`%)Rr;c %1Pp:M#9.G"3!3<;5tBdO;6kKVneJ6>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+d<ku+E"[%6Bmn`OHEhT,`XEN:4Lh^Xsr %iLlG?2KQ:hPal39K<:4!oF=CBZSRQCfll>gE(2Ei#A<H4-b&W+@]<)_?SRkj?sX+X-(&!t&"Hoj9YI%a5:S>>*fL^l(34_bbs,.; %4YC2V<<lj//PkZc60HB_mJ$99Ba#S\DIF<"Ej%%36'bQ06FfWm7fM2T#aU]6Y/;Y@Eh=Uc[s\39R%GARP;dA3!Eaom`eK)E*\htl %;/=m<b3qO"'TDUs=kfkn.(jK\)N>jN,?k3s`I5%t>[sAk0]Vn&<ie(g"gKY,2$toQQuo"`EK(FZpqOTWFe9)lZW@.-5m-3`/n9]f %!:lO]$,S:(^>=I7IN#sf<QPiW$)enEOW'-)RTS[k8RN+R)j)gD!X8Eaq$KX1gr)V7K-f/YZ4<:\5.5*:':CYdF'h2TjX-AY_%sTQ %<p+-V)8UPD`=c<c%4mC8bc)T9V>sXg8>inK+KN,k%SQm.9"uWWRMC2QpXM]5578FB@VT4h3qDGV9ff>RXT^M2/a^\Q'c*P[XSi8h %^a`l-j<+!/WYNHL)Zr0cYf"7%oedS6<dk\^[%-nO=X>VR$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##<Z1*DHa&2)+W"V)o"+0g'gn4')9'R<^@VEp[fVL',"U7kFCTqpg=cZ8GQuJV_9\1WC&8*^H.:K!%GhbZ6CjF3fQIcJ %Ia;_Y!cu5!AV#n>,46%^T;,H64)cH<due3X/[0!O^ucTc9L_\#A@;ZB<BGM/X:lu[9EE*G<JS-s9hF)M?]qR`IcL>G7c;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-&B<s#`Q&K>WEl*?^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_<P&8s$7ImSnB+Yn<EW6)G5)>Z>9%qZ %A`P@J+=-6jGO:<PGu&ScZ[&D?@V0*0/S;F`<+%DgRG#FWAX@A!+RC4=n_@)8<?Ua:<[ruL5e8gU?!K,$m8gI3b=6?[>)Ai-]>8l* %`/L%gQr--07196Z/5bJE]a%<m2D4)QNT4l(n+C"p(W]K4>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![TgcaW4<TI_ANA3=#.mGI>m[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_]VBij<pNnW;Y?5qG_[W9Eh %G3:@43!N4U7\,[7%#h3A1\ol*@6sB)@5/^IJ88%%/0BHsN8_JUF]9!T)An1M#4T39J=c#GO&.NQo_'tN"h$pH9+%VQV1<lo#"E.M %+Fq9X3Xqm%D%RZ)PROZ.E#muaOjQ@0+u1o%kBmP#c&56):b=;35&O2WK!WtB%1L7fROs8+-ad4M5q89"L+9ucnFb2fK6;N%Qt*@p %;TCHq>GOoG!7%>Km.\4?_B/CV#fksu%giLg]ftf9=jH[Y\n.^SBRI/V\n:;-b]g<H^_qB.DN9Z,Fu8oc?oUJ8BF:C0$9K6A(TouH %poX</H.B4T"4iHW)&InW,NM_J!mY./no/%u,b%u?qKPA,3NWiQs"t['A'1sq/cnPFXcD>F0_"/.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<sZ7EX<#F+lEX,I"0]Z\!oo6=TRsL_h\S! %>?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?<TUuV]$2;B^6"M',=aK0 %>TuY.e`%V+:;jm<f1^9KkU&*)5E8IR:]1't<8I=l'GU2TpOcUPj>=-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#c<WA3lk_2DSl6)$()WB<A[,4DCYf6r.uNtEL+`Xm3rHGR#55,F[Ip2= %Rkt2S`o]+2b:(XNRh]^T@7E.R.AojWBct/""QZr7S74s'gD\`\oH5$aC>M>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&h<YI?'<EFpW6 %'cQT.%f]kOGH)mGCE:/+k(/nlCA3oBTP[5+C,]@Gp<ETMJ8[\;_fE)=?1,j8A*5`7Z8@Z86OeD5Aoa*MW>gYiX#B'O=gZ/$W^!F, %)dpVN\:k!;)DfP0ej]M;T3#L:WmcQ:`ub$K4u[hID4*cVFSu\C^1l72#lb@T*Xi,*(?XbW,-sYJhR""r<HUQarcmksFK;6pgUW^9 %/4oXal$SnoC-upS'eh_Q?s/\.lPI`CD:/dRG5f1*[h#6&9jU,FMO*QKjY$E!V9(jG^fglF.H\X`P&rjDfqtue3(T3&q0ohKHjqlF %<Se4o/Z6(b<56+of*DWFfF4WmrQt$XIt!>1?+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<ENf`^M<!k0pK0A-Yj;sTT>@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`Dq<LM[PR$5Vm$k4n0^,ATsn)4F"3a^HSFg]eJ-8rpbPQKKtU0@FkpACC7UR#n= %j?A/9a''NuKY10Y/#o&9r<MTKQEC7G*;P,@o?7^^E@dE_!%u\(P&4;(a9SXg6Je!Z(EJ^0^lk.[^HTdZ:d<9BPMS&N;p)s0ks8n: %p;6I'#0(&-9UH-aOG0uIBM3u[73r)Y]Og9AMO@n4q5Edu,o;+(+PfqYTN#g.QCZK5jO&(r6^UBC%Xt,Wi>IkRSr1a*!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<K_,N)@W.FF1CpBM[c<hM/-E/@2Z_Mci>&e</cLB:=s,b6Y@4,`f*0-TX7Q'; %01u<L+0:!j+O-SpDq`5SUJi]!;;t]#A2EPJpH[Jj!!LP]&"XhKf.JXB5_e:`.r7MkcirdsFm-#:(6faKUM[J\;6GTjRhF7HO@sRr %<j]R(j^3Y#iQ;kh!)^^d+:gk)7B]6%:(no%TsrOp">i$>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-%<K<3#dPgDM1L>u57,B]BJ:Z:=(WeM2a[]FHQ<L"mW8=s#3W]s^rg1tm(rt0)2(0Fi:H!DI/M`K`d,e! %FGgRH5B4t)ATLMe3qGpZO^5P0I?gOOdTgO>3Q*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^aX<ORncs"EPAC[+1[r1AMqf\u=K0qJVcrE+s_ZG7c\?9Ve*M86LZRLAVPJgG9Wd"&Et(o\LG%<^ntp3]'k_Mors %YFM._YC7",'^ul[WkL,'Gesu+N)4.mV"q5FCl1ILG5Zr_K@O7V#,aYP(*^3cTblbl3M@FEY[SPFZ`$Esr6`pt&,WhOFSQf%d4h(+ %NcRg.E>lZaB^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@'<V9?U5n.VDWd^:Kg1t;.DOsp`NM[%^Uj97/En;@[!Sl^*0U$uaK-<bdd#<;*[ii;*8q<*fbi55fEeq\3 %h%$FCki7M%#OoHtK+GF5eNDqO#u?\=-3'ek+9;H7U0(sAI'p@iS9o3,'-'foXZT$0#$ZeHf-$gL\/EBRf",cB(Ti^abV..7g\5.0 %Q3L3uC_opkY-fiXU_^IQM*@@$#?`Xn"hA@Q!<pZa$I!C)_Q#&gONXo<I1*ng3=<P.f%Ut-h,7'U+mf=,]&/#l&rZnEgJSr$Rr.bI %+1t>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+>7ccXbs9354JG<a3/C(iY,KYpFWUs,4fC;$a[t8Rn(&TZYjQAcK)B)U7]gB/ %;Yi4(D-RFZ[tnB"7uFb*0=K__ps:4@cT%j2D#:Oh!GWr3USLSE4G*HFaFdKjSUr$ca!r/gpcrrcV+&(M\Co<'p4Wl-N9B@<KP:#G %TJb[.iS@?b.rZ&qq>O)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\%<V7EQ_B15'<;4Va<^,WUcoO@&*ffRW7X(u\b`fsYb?SJQ)LlJg&$mZib1PM$?A^Gi4(3N#28klmL?<4&BbsZh,[i>"9 %+6n#bOW`b@'(rBDkX6d5E#BK*+WrYeMI@1sZ'Zo4BZAA^:9q8$@Z,r3s21UTm];+>2i<LIOkWD']n:f^h_ob%4,=FkK(6&R.$FIg %V:\09L):S]7Z\FbE;F.Y$a9Ln!@+C3X9$9Ogd/kHdE4bc.+KQ^%ftWbkWQ^-nd>L15:(Ns#1@YA5eh8e^'Ju4[e6</\mNX:eQ'3R %<i!$4H\EB,\k:/eSsf:QO_OH9HpRgbd&clZS/dRq<tfYC#Z/uREBIEm$o2RG"7f:hUB_ZA-WXFZ"cPEVYmqO<U\Zth$H%#(b^8jr %=u+5VI2Wakp`<)7\)l?!f3fT$VW,paQLGNlUS+R862<u8[4IA:1=3!nWn9#u"4ijVYfNLJ\iQ<48b/-$O5GLYAm%#OMal78O9o9% %>;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#nR<a"UM_45MrFe-F2>ArIX,"VP't@5&f=D#:=sTF+D,!9<CFd8u.Kuh` %pl^"ub4*q-K/GFe7,:g3;UM=JD;RHkc"oIe&r'f,qlEr4>em1lNTsf9#Se:Q%SJ@rhA0Z=J$drH5^l/q<WlB?Q`AP>F(:1XDbe4Y %73^h<A-bR.3>(!_BNlR2T-%S/*#eCj%3UuH5V=eHeKcD85UlVuMijWo!g$R'if>>*ku%<aVoQ$h8c2ir\O$FXM)bJu6M:2M'Z7om %m.4_'=X::k+-*IIGW9T@A]:UM?UX$*d2oGB=l;&Zk_<)mW^?+=JG.mP_NL4c'M=\;eCk\ZfcqSgPp5"cXj?^LR1oMM7j#O8m<):X %*b9(:B,X:@gA8tUDcVcm5.0LgVB0!USYNPuJ$V/f8H7mdfAY\hl`c,@0[$jMfW+u&K&,nB-JH<LYbYGkgFQZ8X-iB)oaQ.7g;[N= %frYtJ4q(N+JIF*dp$6+@cgt[O:ADB`X;1AYV.p-maiY6g@urITkQ#%EiE)fT,SKD7b"O1R#-#T7$FZoPl:.(cTIu_`/c-/R/_2D1 %cWT>m>*iNTqB($+C8T&"cGfG.5ai`5.&@>MC"J-:]qg:b5]d%a[)Lio@YU^*9TR*i5WiFs:D4Cd"3Xq%Y94?S:]qJ7?CE3"$VL=A %kbe<pg`_!:%/?q^0mK1&b&lJ'6l3!FSZ).&Vc;u]gcru..KZ&<$@jo1.NfJp]'KKoab6N"H!$r_QE=4d?!Du'A]q*M29dq'V($B3 %+s^/c\s.<9V^qqQ?o`M.p2_\Ioa4[Hs6@;"#qBjo:*-C@Ush]2(R/oFgSRJGWc7JC69qF<f'>pDE.'Gi18i]KWC'Rm+tXcs<RKG# %`AT>+XA6l)XP1]fB'**Q@G$K&nU$Q8:,@.`<L/Nf4D+82Fu[OH`[)FHrs7lJd#X:VP[5539'QlIZM>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<FkQ`Sr[h"Krpb2L#Itl)];#WCKdVokm;8D-9hR.iUpVd.FfPSul^$>;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;<ph3?(X,0-2=<1CTXI1t %Usr/<G,,<M.0fOQ#4bW.*E:'_gB-t9UnBB$E=?A(egiS=I9j6sXL?B57scaL(g`M@*01bY=<2%[q:<qDk!\/daV.+nW]VM++cm[7 %<A`/#.[lB4+/KX-0!AGUC>Goi1_:s3eLW;kZ79Bcb+N?cXq]ephgC24(2L)12\<o!"=X<c]ZOa5"g$l`^fhrp3fH=U%S?s3i^oaa %Du93CGd.Z<d!hhWY00PS=+;r=M]1GX._igHZ%ClAI1(?P`;"2uEMbd<"$&B[S#-P,4=mD93lA6K0RQj;qPkZV\^)j2n;MQUgsJS] %B>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^5T<i5B\Qi"BLbZ88R,ZrBdG], %VbC9a3X>hWcFCDUB_@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/g7<WaGSYH5$*,$2s=h">S\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!+<B5/GIP$QN40iB3*35j&l<'\md^u5^3RB.kJu6+Hgp?2Cn9A/ %)$)(tXXG8DV<D"c5j_%`I,'@)19i\;a*Rhh+Vd2k0SCKefmeI09p>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'><bWj\(^VP#2cAeL:SJQBSB2diXe*R6dKIfQ28N<4buXh#f=Ie=%]a['GcU%D@j1"1:r %7T8q8SOf"g=.`M[/745'^5QIGf^sW^IuAe;L#-B?j[$/0'T#&*65=oFE1oK$FVIu@>R\NqB`Z4i$:0R`k.6R,&mUS,@\DU]YEfT* %n@hSHaX\kC1.+AMKpM'+qH@-1#-K<q#,W7?Q[-<A#G6JnTH9C^W,<cNk4<*LD$mLpMrbf4bHa)Gmo[5S<QJM7Tsk2W6RP,@jJt!^ %)8$3tU(6rWi,PieV=XL;b0A82D369r_'CQefU9Z's5m1(;28HPM@A:C`m#2<:`,$U#"C2<U*_Y^RVj9*qng\XfPch#??"a##-1=c %W'4J&m(J"m(M3)VPMI^DO-MNUn0rT-jmmQ7WS,#9rL>FfZ+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[^9nI<mnhV;K`)tp?,i'8Dg0F.@'L&N=up*XCd.X %(9>Cs2*M<nU2i8]&KWpt.O^)i!#5JSR6;sCbK)ej2^D5/0O,.n7u1EDhu=\1,usFR=q?D+V.">o!2K-(c5s5T<(je#QNa5=+3L\4 %g=!&H7Yq1d&KBNo]ZpCOLNXKLXu6;pmnmR^JKd@q,muiYlUh]1/gF9;#h3bLg,<m,);rt0)KGE;>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+gJ<l8Ml_mHj#257hE!eoQ1"c/YSoLj1J0@(3CEO73SP]HpKFn$27YnX%kIae#B7ot'o6!i<' %NieuOMSB>rm7+<s)m61WYH#qHhD`!WnP3"2C]O/"L>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$ZZ7P<Y*!(SptH`0oC_9l\)*%IUR^WE6SRCN+S=jTkj;G"R"0H0]&*4kLBAr09$&Wgd" %/"t&G?=(+Dp/Rc^\'3P`ZG`aYM_%=4O?7+D6*=kfJI7KVnPK8k6UZE0[ZpoA2$Qr2:^'C$5R#ukpTKplCH_#+5juO)DmK`-2d`Z# %XgdA]iUk2pPhJ#'0'XRRi8XORZauoLJ#J+TV4,97?24iomJ6Y&Bpf\sjTp;hf!q!L-sOKgXd4N@/-ajn&DqHQ(UK+0Xib0Uh8&][ %dRg=jGP/A47UQ%>8qC+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+]<p!h6]acLQ>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+r<Vk6N++I=uEL>IChMb?"]q\t(01"-WeEk(U^9h,cZ.dhIp`(U/TLU@ZbMS>9T#D)RKq^15llJp+W# %)!o4/e`<-8eSLC#?NhG$dBhL89L?5jbo]$6%5@.&#4JKudFtlY7Vd8us'W[k2BosA0FV4nFt7+GmGQ)YUh)_GE%PQVA'#-kEKRWD %)0h7DO1)X;X4&Aa8@8X!2ddIm'-)AGg(lZC]L`5(E_6?YpE50ap0aNdqrJPt5W>J[<XX#J"+8=9'1Z1hL92Kf*!Xgc4bMN&<h&Ea %i1\q?"^gm"g@du>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-/'-+[<p+jiI."unYR;K-B %]S5'Q228)aUrh1[05&Dl_NoV\#B3\HF^>m/M8,srO)"YtY3aU#fA[B@G[XLb??JZ\YNq*LmFSq=([Qh<m1-"[P.[pi91deEDh\@o %or5*K@O[3s-r!l%J(]"e(a;H'JE#dJ7cj-N*Ytjd!IEZ4m`JY(%O0V_+9fDpqc<"/K<L=Gn"FLqLHo52m(n+^P=-r>1+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$4r<T;X8FWF2E;,;Q:Y%V$CR>Y?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<N-\o %Zi:?4T%=9Un#aobLM>(kQD%Y"*!&\oJ5bNE3/5W'045I/o+mpLYE'8bL5^^YC4X\]^\WcfpYL?$\m'mjW#rC%;<rH7@LU7P+DpF` %D^3G$#Xs*15GEYZoIGERbgWUZ5!)H!Q-<5/ohk8eDqf^cPosCVm"#STK(O];UY8do&b'QqS=3[mN?qf2YM_uE."TX.4l+jc+7^1H %6N_9E)@H94!n.rc,3Xr"CQ'k@dA]0l2bYPhlWDE8K;i9"cI#VmDaFD8YOa[,lGV#RboM@F0\f%fFl*[&I`CO3CTr2VqW'X@04/oc %1YN6f!P'8PV)g*\.W]j)ppkQjM9+CN=I*YA>NW!blXN?/l[E\Sk>n.,F\_-l>3Abr<ULj'j/>nc_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$<u;\u(!jHE+1%^b^A-,IgD\Cu8BXfO/pM\tpB_ %d0?bo[WY#b$8CtnLf@kInm]l./MQ\IB5N73?c.*.!1,RUOfM:k=Z!<`M\ZQeF`%f/0nN82f0E,j%fm$dZQ^Ms`IFRu<5)%=Z%6Zn %H?TDUZc%nl>/nI[XVoq^2_Y`IHTT3Kr5Zh`9A="gLO0-(>AMZ9[>-L4:-_Id$]X0I\+SIo"\f"$_a.kliT2TpOKT&]<</QI$9guC %*/rP]37$QQLLWCK4F?3*o*__g!2KOk\=fHGn@:6FQai;j^_MUecG7U2#)EMpK3.4*Sf[0EX8DQNT"Q:Pf^kV!\AoioZHH8E,Q7A& %oG>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#<TL)rpsWQiGJ$+r:1'gQS3<J"4*9j\FTo&WhiC6%AFU@@.8?3EsiiK %D%TsuV.LQr_:'7,n/cDLOF[s]jE:R0%]Wa2,U:J_A15I,i,Q]t$`OhZ/aMuuY[C)u(2Q,^31$q&LaMAIEq&ZhJ@$niU]:Hg5`BOl %Zc0!>[L/n]B;*P.,A=\dE-;WkfX>X0gNi4mnVmT\msOhuPq`Nfka"q`QOFL@Ml<ABJ=ji>^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#b<jd;;ljAH`Pg8%>Hk<+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<r5hdHDU/_E^OVZ$!%U\ %]1l>=[:PZk@1HrPSdXrb>75Cai'IrJ6XT;gMUcbVj3unnHmc0`qt&p(NL#lE[kYDTgkQtA%*mMqDsRH1cGgs1s&Lj6Hog"_'TT_M %pD@JB+0j6Z^_!+%:<oKpe6h#Ee6<]U1*<@O$kr(gc`bE]nPMLT.nJdHR]CmA2M!q<1)2oS5GQ+X%:o.9"s3B5i9//5p<d&5qi2MA %l;8I&,QboKUnAZjrci.HQDfrSZe(7M9PVT=Zpq%NpDN+OhNbJ0VLbiE]q0/O9hVrbQ@VfdQYD@&38!5tD$b&%T0EU3/ZuQ/=M\!p %/SXs6M,(^9mgot!s!:MKeuH4/#M1IQ6UlQtoI/mmU-n^2g@i+.>Ue4Rg5i%.[7d8n4:,C<P+UpB<aW1.5kUGoDQ[oM3u:!4Ag8>R %S9UE^l4;3PU17Z5X!PE<1KU<klD$1TfjB27dm:hRl;L:H6FB5R_H33e'%11"bAn+X=)]Id`':\qBWug1(-,E*-#IG%7;>Ps-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!<o8eVr1(%`Zlqk&WRPs3.m[Al3J,.-k\U^OO]AUo0Ur5;]15EU %q/R/5CkOgP8.jKd!%HOrMdiVLqj&U:TU%;]fDZ]N99E<*r[RRj8hH?$pS(b_M8)rSX44i^*b>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?_W<OSYl+BJ3:+qQ9"QnT5@XY.G-n.aTLZFC7\OVqtrqZ>GOP)(')*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<Y7A#kc,5t1fV(04=jd:N]cBi0bQW`K@2.S)hi`HH@<?<]Tl.<nc08kRRDD0?I/-,Dmp'V;W8VWskJ.t!!F:rprc %bTbN1&[hJTA\dVKTg285kluJ'"pG^b.b4*o#-qG55<hW)21tlGbW>+(#8.U>[Te.4i>#`lPVUjbJJRFO>W>P3DQloI]f<uoa1'83 %W7Nq6Qr)&s8%UI?!\D+<idPnSWIe.PMHX<N\rmheC+o=P\upk'?ZueMjp_M!]s(M[]WUL9_a0`X?j"m$9#frV?"YF4GF&#:MgS9\ %.5!K=!am<.9FlAsoDE*HNo%m"`q).dGgDT[,5LU7J')mbc_-Y,cC*b&"7MDWJ^<k+>laWW;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[ %"<<c?.g`[$:iH_E])nb:3<m/K_)%H&7e"WP/s0pQD5`fLK4jK+b;"ql5e[;.EsdV47Ki)"!:r<f&*,RAN6e3tJ:!p^Ljg:IDUVe< %j`%9;0gE\sb'6)@D=P="=gl)cI<=6(aS#$;B=`u`.nj]M#Me)e1OuVN'_mO\^)Re1<>]2UR30[:V"9i8`LD:K'C5`-COL;lG59Bp %cAYkh'/?KrgbraiDDq/g`,@3W2Nk<M!N-1pHE^">B1Z<LX=\>o[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$<ZrB5UXOqi$"8T6`cBW.#Zj#5,ej,83&al92%$'teb=XhDIkMpf+po_X[k]HjirKkLpA1Q2 %Ooj8jg%K.lEP!:.l&`<3'rlUh%?*i[Bo!f=92T&i4q/Pkp7T)##@(S:"e6GLdGR,6$C'"4_)3Gt=s$X-I0C5PHr8L#ZK2mf"h0]( %dn;tCnAhc\U?sUMi3sXo#8,FuT>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 %<hK_s*lo_\hFcN/LPAL6C>$U7M`?i-G0cc)C\K7(jOmGg4\76/@8sT@4qU6>^`@Xdg6;k@!4N]RalR-4;Y)I+pVUa($g,d<Id"I$ %cXR,c0!9@*rm)\dbQ%OUrZD1=J,H5EqXm)Pli6k>rh'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`IQf8l<MB@N.^bQ(Pi!MbeOcu?5$BFD21Lfo`K*\`bgosUOk,- %7-l:6`cWB9@pY?LgBJkH`H82&_0#3P8r2-E?acXEXHR(Q^56p'&MUC9!_&ImPUA8$YT1<\>b.W%-dBT)h&S![E?Zdacas:JeB(BI %gNVRqfi<lPIqso1@)Zdg;E+d1)uCL%ES__='N'?fUm['!W2&iWB&J'q*;+GH&^R@9(MrP80`$=^ra/`3P\)mOB=Vi)'K[Q[WG0Yt %GSpU-4=`!]`?$`3ciokN[rs/M"Cl=`rrY:MN]"k+cRmdQT!;F:cNfkZq3gS?lER@mLfq'W$bY9gqg:i?oHWb*B"OF*Omq_:YZ*;5 %oQf;II'9pjL_bo8pGY!;Q&'Zg0+1HckDG5%--hAJk;clZ\RA/#!(\cg:q;niH<(\l^@Hj6#CF6rTdA%L[S"KPZF-#hrkW]d1Va;7 %'F$2e8hekaH&R=Vf5Lj]5N&fk&Bb&AbM>N%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*UJBs3<T<$2#DN^(=NAYB@&4.?M1^q=3JQ\`E$:Ic&R+5-Kbk53mQq3QJT54EF_rS'I52`^g[ %iYr)P[p/qe]E/f;L5"P;?bm\!K+RP^'>du!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&<nb<cF'bZ %,`ne;F--H.?4eam6kral!tuQ]Sf8pld\tk=2cqfAmFhXV;)R.[j\PgWIrO;sD^1X[4\*H,'WC&5U\2=`8/+_NHn6on(tGo*bG'Il %n6A_?aZP;L@B3uiUWnL@DNo*/JQ;jb*+@d1P8EXJC/LHESud9LIm'p0/XYLeS30%ajhNWW7)H:i%Q;$lBDo1`(E[BG^@LTTLj;d: %/K!(J>c.^gZImjn@ONFGLEa0Z,1^<K)r<IBX[VYCO[g#09"rWI'thU53R9MP(G?CS>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;. %(<tJk1BlFB[Voumk]])^op.i7(V-i!Y(J0)cXWeHboBcBoj52Jmb'JIF1;d&/\Q,HPKQ.23)LRY@KCX^9<SKG7g1@KGA%Zb"-f07 %Y55>0bUWddQ^]0qmb:gd9mZLbcR(,8k/Q5mWOg2SBPVCpnC$W]_T*>f;na;U?2(2F!C_B7(u]<<Kq[9'7D(4tn4adUU#\RtGZ,aE %D(1=.Z4drB%n.tTRROE,5lkn^A@sLlnPX[c,/,MUn[>769DK*UGZu`[D-<!f&gSX[D8DBR8t3JX5m\-AMl(l#?KJ!t$Uq][h"%nl %b>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+U<F#l^5/8`s*JSF\WjIUDl`S)\5UM[57ABgQ*-VWl<ddR@fGrIKN% %.jG:;1N(Mtj*G4YHd;R\IWC?2BUiAHCl15_IGhY\FsnS,F,,`\[bQd^0/j@;m`!;Mqd+."h;QTLr42m;Y`tV)0PhO0*XHYO)*ph+ %(F!*r``j@k,A?3Yd+,eo`L\52GO4/^^]n*_:?,aT,#kk44bD>pA&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^*^<Nr?Crj/N+1KnXX-o/k6cV,=XX"eL_$>cdNnH5>$/qVdfqUSHo)raK<p/jO_EE[$,#1 %BhfB4BK`SZZf9jE5"@bLk27h:eD7JZ5<IOq,@U:t#um.<=9;a<F*dYu!1,4S_MDA>&<Pk#f[jrQ/d0K%Vj62KM?lm!@kM!T5Vjfq %3/8%^A)O-T>W<&YZX./O]f+0]F(]:`YGWg%D#pdu5rXpjLh/^mgJ@bJT/#-RHB,.2>3<hjLJ4f4#e4NoF(M!2"IgRO:]1:2/39<o %p'55I;U*"nN=#[6\f=u-?XN%D_UH'.(C2ti?<dl&(8uqra$,T^CbFV?^W:Sm2(L[N@(<2l6laDR#$rtl>`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`b5</ORGW %h.&>Jk_@kHl%[HrK6t/a]uM0`iECN^eU6SY'1>fH=NqRc,4qUU6.u$#J._C?i#gW5'H6uRC<.;QT=hTp$3EKmf)YP<T!tJ0_.)h+ %2Kn\^K6(9c*F'E9[-i?G[*.WhS!NLgr<4:6@61P-d'$LCnb@lP-UGjWq]Ih74,AKF2l7Tt/Fkh9O,,fOfsA?.P['i!?RXM@F:j(4 %T&SXVefPCY4E5u\fiPW=Ep*n[X_>=))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`<<K[\;aTR\fe6s[\T.Y3#F#S4`?5NJ!!&4GJb %0_cLKO\!:tF`!sR5G.'bY8<A(D\)BO8U5I7"Qq<r\<]A+eFI5j6Nfr9%6UPmS6J0CbY1snYN27sp_C\\7R!3+l/9^nG$??0j'^%f %bu;(t>[.NDUSuaH`6bk=oi0FD[To1GD!.Z0<@;rX-9<STe8MemagQcul.@nGbFT\HAOWsjpf*&6bb5GJ:(g6'CJIrhoEeluJQ(go %_]Rm@f0T;.In8lo`=Q:82rL"[;%MHD;rC[!:c-mHUTSk/2%1<&qTe3\>0>3^7MWi$Jie>7!aaTe8HM#jR0I9%P!B[g;FVPfY,W\4 %qY+g>/Jh##,0%[>q.Ar<U58J+n8?]<f46m.c0ts-TTW`$,]JdCj2Q2I'big#QpjINf+r_VPkp,cdg#*:/LpHS\AT-aX'X*(^gQ`= %ane[K4:AX_#6X9Xjq2[CK-I"s/M)^]3[T0F/>Hrqk#YVS.ZsO=Y^b&/e\2(7UNj'Q>f(pa*I+)i]nHAnXk)jX736&X$(@dG!2h^F %bVSqH2o$^Pb!Fp<XYN!Gk7Q`*H`0H$jrY]i`lGn?_(b!hGfo,_iph:80`=%e9_Ft,)uWp5Jf,DZDmL*U4Dtb&.R\sgq5iDqT>e`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(cY<?V)cgl"fkQdK#Vtp1rM]A"rC*q %J8in?`c5H2PN;oR&KW4WZ';4I\3\?TI$#)q((Qp[lbtdB%qj\7/H76u:Q@#+MEDTZ5`lV,J#BQo[jHqNl!7ge#<>JK7n<&['^: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-9IHGgh<Y2I$paMqKpQFg4^Xj`6<Ui!f3Q2? %-_G;c%_Fg.6QB>YOA&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;<Y>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&flJE<f0b&M+8CbZ2;o#:OKE7<i,lD/kNGXIrRf^gd3g_J7qgnrAWcG'r[)\rH%j.ACm0dqbNUmN7:pH6Fh).fp9Pa^ %l-pLZbm-B)oC"s=BtfHQ!m$6A&->BYpTAt+@*=FR5fc9S5[&Sg0T?FG@g,e]fs)_Sp/$R!Z*D/#KN7NM/&J:(L_]kcIpa6p7=PS5 %CION\PLa2>So4";df)qn-&0(<Ad7*mS@20<f]$*Fg\33i[mFVY9$lqBZYorB91Y;l@<PG'ZC-=LWW"T'jNMH`-jj<MOJK*)K.O"f %7Pr$tH;&![G@%^2O01U3VBB&idLoWkA$7<L%$E+B58RbE<I<('>?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)<!D^+.%?;_9\4ab&:Z0J]p,bq<6sQpHCQa>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:l<eT0FG;`^DJ$-@)^D$$`YEEhPT5 %=`;8=)b'`m#n41!&//Ho&sStL`iJ[1WL,%WM-T\<XllKucT5R%^j;_fRcE^`aOUq3',bV+$r'$17drfqQO4p[ZS5C0AP%2TIcdUb %,GB1P':#P5J%.F"0?ZhVe=?e_j%#PLEgmq]7OKMol<&&6l/Hi6c&-:,3*>OY^.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(3<C3Mi^c/Q9TS:H)B14bF82)O"Js'LQ4p#uQqG;lO_iW-4E+mL"8i#6 %ff_HDH+`TQDF-JJ5'"7a/CW.aZq]il5[o]WMQj>F$+#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/-U<UZ&?nQ-&Tm28-ug:IR+24\V##A[*PFGT3UD6_ %(]<5!qkSf+[c(g"];Li<7]rB>9S=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_<ET0)7++bCeH+^bF3YQ/@Mk0i %)/j`*enp5Q5_^b;?tXsXbWmrR91'QN9#;0a_K3o[B*Rc,=TZpbYf<%KYh-PY5;4'c3Kh8qqP39kaLj7aYDCo>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=<Q7AUue<\'^mi`I?tj_:uXL&1i2<[\2+=`1`"8Y>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(]10rW7aa5Fa5<PsU/XCdWI9$ThOS %PO]5)["AYVY=h\e3p;X$#lVZqed%5;#$tR<aAb;LJ<`Y@+?5gKZZV2X[6TlnT,I8XL@;uf91n(jGLI!=>FKi8>eD2`KAb:3m7<.+ %K$X*ZN_Y*D7@Z/LR7MY9M0;O#eA8ZA(2S#W&f<>c:)(>n+Qq3Ak1,^;*M'gZ1%/M3lcooo1fE<p;S'!=7TB<p#o@=*S5"#ij(k&J %7gVN!eJKH#7HpA:J2:,E2o``rkdMm3RcH[(9&"/+r_$OY4(%cTDuPjbibs+`?T?6phWrp"r*d9`F["uZ,-ZI>6?(i'3)t8f^M'f] %AEKG(\$A^>i=$7`DnPoeS3NYB,"PS.+3MD5Us:L!f[mD'XFW-.bO)3qB7W4hiQlPgC^14Lk$t.C_37)'Up>@lBf<d&&GH_i1Sjs8 %QugI1a4$$gUdb9Z;qb`X(NX0c:]82Q5>604D%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<ESe?"NGN8<Y`DSi!UYngA86qjD[W!WKn!T]Go4=L;pLO5Jf:X`k_(P]Zh( %^S*O=FUO&((7)U>]S!<lU,Pj8"\BNb-,ItVfCTB40n]j/54ooN.0?sVIM3_#JEV=F`69hiDrp(Dj<Z)^*<q<alOWU45=dX/]HDo9 %^G77r^(2J.[&D9>6.o#GRE6Mr/cZKtBENore9OUrbsOe`h"guTpT?TH**7=I'D+B9:Ju@5EPELRr*]8D$lLpg+aMdX(kt9jgCfLK %-:*dDiS1![H`sh%^.XUE<M#<X&2Y"[O]h3c7`K"Hem?PUSL`%ak2`R?8JG'gl`2&pcM1B4Y6MY,1q#,e%V@V:jlnOS\tu\O8O[b/ %=Wq#@F&L25]&ch0E,AlGTA!diq[Z`1djm)WXnWX-+E7@s0;'A+'+pR#,U4,OSMZWrfXT9/deBCEH0U$+&'LS:C86"HgKVIaXe-01 %&#5<;]<d8s/4#s-qP0Up#iAOE,j*?!TAFtchiE8F2uiAVjbF'S\]7kKYdLZf1=P-Yak8USn(eVeHU@SL?atAE`3m%&EdTq$.[>+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[#6mL5d<q>8)PWX/"c'cXKN&e>>)$qPF<ar>/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)"*<L[X,KrVl1KIe2Ca^]!TDTAG%+h<u\Ao_jRFq#7>Os7Gg$Dr0IQ %J,/e6?b]+S-SKsopuAuq2_X(&rU`Ogq>C$V(Opl'_sre;3<&g0(\K-3"T?X_59145q=p<VI5LoqVVM*uc2[R2iT;ZdMr-fihD4pG %!>CAu7@FD#&gPJ;W&l6cZ;)(Wn@J&,l>\<Z0EnMRNhrC(Sdb4S_@t'(@4^o3b]QD9@_=ErDk[E3`jBY\-RmHign7,X8CfGJ#1MR5 %h9p-5";>``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$^adq<jlh4FZi#dn0T)oABHnN7NLU?f-8*@$`S'O#?IX2h-5P %LX1H+s)>Wfn%\neh:k/qkLgV>39KTE?_)V-oRUAf<e:5<1"n[%%BD&JCZOpliQ2HNl`f:%`h=XUPVjS'?l9%Yf?l:KG`'PnPDJIB %MIGO:9.I.;pGHo<%@hm+T:>iEI/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*<h1!W8i,r\s<tPRA9fWaN$MO5F#'_07J>.m4eTCgrAt1TQ)uGpkh<j.7?9/IiBU*ZRiC(?dL+* %_,)WBdlX%1&9oQnd5=#:=KMZd+_a?+Y!^fQ=D;o95'"1HdE7+:neHV:3HK'1`qlZ.P9`4T\B1IZE4?L3AN>55Cq`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/(GXeNS<Qq%f:;<.jVC %7`MGO#G_pH+?T`&/[SU`DkAfM?WdeD*@E.U/PJu:.lB.I[)`\V6GpWjB2IPp:esdt-8[$e<!p/3!0stS"LX!ZR;i3:8RMZJK.'0. %q%7.O4a%_Oo4U\0ZlaccL]OQgG6/rL>p&PJRai,Y)AZtCb5PLh$/ln/A'n]U^"<nN1e>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&I2k<R>U9cJ_u91mL>8 %6l`MJJBlGL55-_?<PR`LIE3*#gN/M&SKW":/JBb<)3WUUK?($_qt--:Yu!$AV-o'U-mit^NGM6Wl#+c++-s4;Jqe)0g[`=0"A:A) %<.kSck(=e3KK>59)!@(_G*)>qC7\rZ,gdkkA`C6FB=0S]*j79'@7[`Z.a!NTJ\XkYN[nW/<3&kOFN4Aq"a;_ojKQ9"JUG+Ql<PPH %a.eU6$'KF+2qe2MN@6alX,/5I#]0-6dk0Cr3:CAT9/-PIl/Q0Cc]=WFj9tpgQc'+Ho03mSrqh:ll@LGI1$"C&$F6Qg;>NkM5fm&i %/_#QDRSV#"b[<@/hcI[iLVo=naJ*>Q1rliSbQmp-e`a]b!<>AWke0m0F@W30B[3u$hWimnZHaC6*9,$P8cgAK=Q5+dl;!<R0,RcO %kUBX4,^AH&F>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@<mek(K4,UG66/YR29e3,Cc>8:5r*f#2jEon)B.a[E)hM\)5PM$ %.EP[FBD4]a>\0-ST-q/V7D7DG0U:.*-GZX</ecHu#$FW_^%5&3hY@I0+dY5AjOFf"4^`@ahuE\\J8Ee]C_Z$3A'@B.7G^Ml54Kg> %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<!/taVNL%aWLgjIEM3+q=NjtAq05El?@,![(<p@ %nhT(bLRD%M>"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+K<TCOj=-%O+rG_IRkPaQ'Mt+%JFTJ%<)U#slPUfZ$Y%:R %nB!+X@6i(5o(<uVLL!TbgYA(=9o(mVs13@#g8I`]2-CPmU2;:DqFSI!nq2GD2YtF.CdjM9aRVM)`*E9P!#Rob48b!Y[T=KQPI^:+ %-``eBTHG)mk<H3(8;sod)RHmjmAFD($B7f""A.EoDUjB&e.l,V,1A'0"=G$`A;'>T=+;(;f2OHp@"ifK7b2o[.#_'`:4%M/1tWJA %<qk6^@,T>k<Ii+_+i0lYFnfVd"7X-bZ6^A+WH8^[/;VEM#`cf<RRKrURo0ACOcGQgYnnQIGnknfMtmGf0VP[#grK;nHpoL54VhRj %^q(d-%bk0p*j>P,bf+,9NWWT[C?O)FmjUKsncOA>/K(=2P'F;Anh2R_;)8V4L$)[R+#EQhQ`n9l)9Ob9=,Gmq**%#/13qEcO<CXT %4jE?iY9NaMb3Qps/1XHa!@jpH=^D,L7+(S3>$i#+Qh7$.>GsP9J5rhn#]bVC3,V?k"jg.(X7'))>!uneJsgDck[GU&`k0;rJ7hL: %PGBT!mmSJ=aM=%>\PW]$WI@]5H+9D'2O0cQF;eK5=9'GTF<HF*+#./3<[>.Zo-BDL9nWKmdJ(_L^H:Ser?+f')6E931m]oscVgK+ %<-T9:Z5e2m]=-nd`]C6"(`W_p39_tW9T\fZq2QHJ#S186\r9L"KLP$qa_<(Kn472/eV[u-?^s$`<K*4qi<[c('cR@V`30L=VDto2 %.[+Oc.9kB4')$n9;llC3LbS-63RE_<TP,/pEQH,kL(cJ=7&ljlWcaZGr=cntCO_/l,oQ0,(]T:E2tM^%V0&X/JXV\4qJ&S9l%oti %/65(7,:9Xo$0tg-"tR[6VK%LJXVpI5ZA**dP`hm.fN@-rR3nR0!G/.bUP!*1p0r:Of7bd+*89h7U*<Zu"*[k/`]g;^VC,aO)rt<o %EMp$-=d^C]_TW+;RoU/'b/$IZCmrJsK!,q?.FDU8P8fLD$@+G^gAgF6R(rM%TMU`&jObY&T-Fc/@Sg>7"!f]Rb<r4-"?mtS.,l9e %2Z_eMPiQ;BbP9c$=_26XeTr6'`*WA<#em7kOd+a!9lAcOm2',Yi%IPb7b%35M"<YbD-a)NMCfUCPIHsLaC.&pe?DIYk($!UngOHm %D2:f3eR]ahlcrV3A/dQnqN"_iN;9:rQO'0'1U)k[7HG-e4]M)fma2%NO(9LZk94S+c)T6UaiiFB*Ms(7\q,ecb+:C+FY$?2O2V3k %ZM+LI>WBQSFR=eA.+<8<ARkq4nKTt*;h#HiT!df>Ceoi\.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]<U)=&-%bA53<2B*;JV"h+uu>F&YYo'A7g4ip(SDi<I##46B6b)Z&8S`d0b`7k:4ppn15.;8S+Ibmm[8pj@1D4"+O23 %lJQMi,C@,SddB52!@lMk4"-X\>8"f0NV&*-rJZYko3qio??DEcaYXi)iN4Nr%9_9DfPr1`;(CY)&)11hj,SD,HleDD;^L^=/KM#? %""2s-BN"NVI#Qh<(tT9hQ`5f1SBpqp55L9<egZ46efOd<5s0-QS/381AM)\L@=f@U;E].I;4-BUiDB]<E]6[RNhK>crd>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<N/L=E#ShHPn9eL$,u^=QoI[VNH9T.i'bI3$Q;o*Y,]cgGJXmnp^L3sr"]_@[/r`DV3"q$`<1_*7 %BuV,@3`J(u8hl2^m?UW[63CAMgbDpEe!8O7\o#qB^TJ*E;s/+i,rK6]<U*$lE1;WEUE!$_qDL[<4/ZKOP*FcLd2`dlSZ"piD2n!B %]H7-FBSS)uT:aF)a;=\W084$X7XnbN+2&62D5*(u"1Jo:OL2>^,\"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?+<Mldh5C5P_40ti8]%3Z^^QJo?;Gd3`UUHH#jRJo7VV-R^/'e).s7Os[8&!=,H7aNW@Isg3cPQWM %nNXeb6#3:qoYT5c0+q_)Ic2h([KO*),SBk+mf)-DUtu!NA0b;-T:V(M(]:Ljj;ZnQTd`PChM3@&a^43OLSD'%1QGdIjCFDep6:RC %"D#^+=]."nI3ro!>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$<NI"ZIY!J1Z-d`PCN\.- %$RZ<OiOLclFX!+8f>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./77<W/Pr2M[4'19?j`jLD\.`= %^#1hOjFn$ED%.t`JqkusZKA;]hYbD>Y`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@<F!r2'/EO.4rP'ao"J,X<S9ce2hnU%IrrU?/KdhBmG?S0jNT19%`!Y&^qI]V0b<Fd*kctl<ADWFop+h:QF %A^l%[W)`t7-hlc'@;DA-0i?Jq"pu<n34:`_fb`,=4h7n-6&Is!fhatZ-Y&!M$93A9#)m@j-#1[$G%Ym1$&0p?U@2*BB/PkMbga54 %H`*%+:&oh6!.1I\-Z)ksYuqK"\(Ze570XKQU%2,S_64qGWE<J@#K_W\:$nhtV!I6[83DQh%K('iYD'XYp&mesP0j$(`8X3>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.^p=X.TNZmNdDaJ1Jf$%!>+T$f0IQ=`K".Lj_RWNJ=?XVbal"t<o=q?/gb9Q@['GB4,A"LT)jegBfR*;K-U.WdWGWjsejeV4Z- %94KeU5i)=lN(,,"hqM7F\TQU+7QbB!JSY'^B+H+'YHV?b98Q.ugXuS2O`8:B51+iJRM]84>N>\cX*K%q_)k!?FM1bU-JEi*#,tEb %YRTV4[`9k-6BAGlc(FW3F+j0N:B_+tI]QYd<V.fspQ)[*<%6LjJ2'`(!K@RgSY_t8HS!JWmBM2>4DgTh&'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-<Qs2ZUhG5blIS-TM_CVAFL8A`aJ^ur/><TR>@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<kiVF:"%/lr5PZW*B'"GU'tscCE@1)nq5r%p')fUgR'+W?Mfl6$'XqfK%a7K@ZTX\,$>+nNTsYPZ_f"G#Fj4 %G.#tVTt+]Ic4JBu(nrGX.GL7_7N+Y.1adP"C)<m6eZGfD9N<k_7!EHXjt]_6)S9!O>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<oD,u'_I0cr+<CUrTO<U`/2do\aU2!Kh;i#Fm[E:'-L?[jcO5VN5>,!`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[4PK<E@XE.-1&t.^jrP?&o_AhOm-9E]oOckCDP`<O\UW %,B0bo+#XpMe!%]ofq[QQ-U]dKEb/[*afJYLM*)E`o#oY*4@p"H)"ek`2aZUT>f0#TE1$Va`+PB4Gl`\m(7FBAaO(EF,8nIF#uJ`3 %N?pP5)9t1mJ*ub&3BH<\flLuDja%CS9DN=8cQPc@R@i5TW21Bsq/c&gn/O_VW=DQkXaA@F<Z<r6NT'/M%?4MN2GD9\G-CEK0Jg0q %krDmTNel^@f^]hCL\[sV42Y.On=>grFVEdkbVPf4JM'[<DC8<#I"St@l<L>]>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<u7>>&m<jp)'R@g'(,'NG>)']@t5!<Oh>s'WQUg6)1De'kN-Z`f]4=bVtp(1$A*naXZ.t;Y!Xe(\A %ihUfg%`u;,<YieXl*KqO>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=,$0S<tA,=t7dp%K9JM(JSesXq$o8mT?Yh-_IhCmo;[^ena/g%AZ)g?aoAdL3o!'6+6NRIE"O! %R'<OIo8Be&bR1l3-\mSm@F!0c/+e6>EumlGQM"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"!!<O@V*!6805V*WdBi2D`fN]I&9b=if!@c]nnPRU_Kd3Q_7&T2"d^M'^[/]hH?TeZ02Oh0ZK8-hLGA=J-Jb>n[:SM5fa %?)=QX(hFSQgb]pqbdA#+a,rl.%>@f<]W/=?1hr4Fgf&\(/F8iuXVAp$IK$"f$HkR8@i%'MDNYupDY0ck&<X&omFie,#fkrsdQ<*3 %s%P"*\nB_]<[u_P5b4Z`#F,W\_#D$7Kf4@ce-E&0P0+:NAS@$"QiX3d7>j1)s+iE2/=.sVq5C5ldL\MuA8.lAd].'<YpH-*=&?%[ %CK0R$K\ZrZ(2^Bi3E*+B<DjS_e<V<9>RL5T@E$<$jF1S1?2M;J9Pe=O*kda&,$76<T0R0p\LE._=%m)Ec";;jFY-7;W+CN'0@kf_ %458M%ZGIk**]d_P8VRQc2cDo(aW$[)e*:g2ZF6BfBWNfAS?jkV?SgAI,aslBlb<iSF]%9Us*,kI0+PLH&I?c_\iMVoiG+R95[R#[ %]OobLV'D580'.,Dl-6pk"rrAqP4GW>NNni*^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^%aRFh<H%^`ekDX7SLH2T%E!>Zma9,g^K7YpUiQB %AGr>M0CT<ookG(!fjgpo@7`H+K&1f,,,n)9Olgh-5d_;EVTEF<H=Wl+G$_$#HJ*c`LCl>6>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_<h&>s"q"dG<k\NnlNE"@tEC!c&K+gSs,TMGiA=TYI`GFp:]5$/Ei0EJ%8M1J!X>?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)<UNOJ0fB6NDY")O1T!+Wbq?`##(+OpE\A)?E'_ClTX3&0+M'>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<AHRl7<0uhb# %QQbuf4?\r]0Io+@gV>,K?;)2^g.m$2<&*4I4p?(4],Nmf1,f2$P$1S^_"1C[HCk=T3"H4`28=!@ehhcV2<Jcl_XK\JjXb`NJb[&S %Z[eQUf8PNdk/Ofmd8H]bf&5u6`8t-SgcNL3coAQ%eDFAm?a`U.C=?U\Xs;BfY)\&j=F`5_m[4muL=FL$+PWn03UfJA;i.GhY3<@E %OL?bs,eenJ!4K)I><BP[0[J@Z)U%M3Z7fMO@+TQ`H\e_Lj4.H-b)YelKiES5%&;2o#g#!li%%DTpoWPrZIm@dRt_f#+b7*lXXMr+ %VMOV%(A6T59lo;ZmKSg%_b?b*H5<?s/p*-,)4_T>Q!?i^GF.P>SS^2O1R)hIOjfp*59WuGP]-t@![JB1ZZJ@*&"*DO&V_@[LI/3r %4i.%D+-<WnC02okb&"m+,(_"Rl`D_3AWNVppAos5P<W0L#@mju3*RQc=?mb%>.u!T##3/pf<Xl/Rr:T7l8(P=P+k+qa*(4bVgI)T %'[Q<U*=rl_3H^R_1$-JGcjjf@SF3l=O$!#67Gg:m]B/psEK[l:M'<d!Vc)O.2]MEF`A':M4'd*A_ZNYLV-j".[bk?]i3)p&fDNKA %?HR%A$87(`E#+T$9hqg"KMTMV41I(I(^!C'\JET_W\=_"=XG4G;O^amRau_"N*)U$a;!>7)<+r()q&"gFZ/6"G'[cY_GIK<^M;>) %1OuVG9/_4%RT<LLYU]pIF`GC5]5@m\ILpt7s19EV^NoVJ]=3:cbOJhEs5i2J1a"VX^O)Y`]Jlk;VrYNsRHcXJ0*&#JNNL;eN`jkM %^A.V9TESfH,fV%+KMP`*(Mu6q);@7=h)+P-=L2ncXe9b;Z0N;.p@),)!785@DQp(,!GEm#)_BBJrk#M6b7c.aGLE$&?;;a>f#A+O %1d=O]85iSM<KY?0/?9VM[6?jJM$N))1YnX)nuXbQef9K8?#?DRDZkYWXJJ0d<t+iI^Ft5S<HCnON=GBKBSVi$^N:6W.0U_1;6CP` %2n8X=pXd1I],,sJUH(^3"-Sd&0p4fMH^oi44_6%l$!O,pg:%q.G&iW+mLCq4LrY70i[i3eG-oh`]G3j3ZLk!BGP'e_>htk9h?Ggg %Zl2BFZQ^q4%"uV+KH\+3(2ok'd8(8nLr,n3%d6TL&`<c\7ep%(8eK-f7?^!f\II%>aX3fh1*<JTB*1PQe\#HDiRFLg`W`K(USDWn %aUkS,e_L'/KHN$+J#g5f\!B*E0WZ1gO%^c,E>=,>^-a]AB`uai#&08-@\kT#oda,g8r-56G7e[gC<!sC8$$m0NE#Hh5\4A'bg_$_ %SV2k$o;EM;bCZk<l?k-i4[=?BS>(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<YLXoeXLG,7\J0sFciYKf=LZe90dK,jqGMaf81P?A,r$en\*Ci:^mcl#U*Es&#V_L\[> %*(RfXLTf-r,6XjfJf)`$U)MN82*j9J>kH;[An;7.(n%KdQkP$?oD6C;8Y)LdgLkVfL#Mo[!Sf52E"(a<b@[j(P=G$7UsY"-Z(YR' %*3<)i5dZS(Vk%X'(Y`IhM4-9),q>CFDnf'%>sR/H!<ZjT`OW@-8SRh#.#D]Z1f`euh.:ZtS2PPI8(o5Cn-XY_ha![963UiWck3EB %.E!6Kh;\:>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;'<Uni-1!T/h[#AYmik?=@noui]SSi9f6B&/C,2\TMl#/ZKJrmi;Z/Y*cLFmiR&F%h^"n6!grcR5s./;;6JV_u9)Aioci %`JXHBSXqr[S9fY"D1ap@a*eoEBk)t&j5$`3c5gtK:uprT6c(8Jqm$E"4P(0a7&=Ai\3L.tmTQKI=R.li"K.T:4t!DBFPp(Ko>.;b %@bq1W!j]a\b"KT[qWH,sZr\XCrUp!1Xf6jY6Vgkd`d83G!7R+Uapts@4[I9^<N`D`bS9I0^\Gsin+Fqae)lI(HY_J*l0+hel%/Y6 %;.?cTT$^5>A51NsE5M6m1_&CPrY8EZR[\gD2l@P[(.G@dP1AR\0c<lL#,[;ZlD1>`_ObZtLgXeuEdS)F_g:lS7bj[D,//m$O;<Qm %ll`3['R!_[F%92s*AYSfU1NQ-.q0P'E(.+h.jl3O%"6hI)=oXM#fW$N>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`cP67grT<dkU#PD3>1gZ)'i1LuGeQYf<nrW=`u5]N %BT7/?nSIIF?XH[9e?3R>-/J9)qBa60%JSMQ90FLHeLN-M#*^+'2'U@%46"-B@qH!$FK6h:`eDAC,<[>h>8fhULLtEm.(YON(6s=$ %pckk-8m/ii':R79<R8\]bau(4,-<X4S$=*<]!EZJ#Uj96q3:Kf_CEdG2MP+dCoC+>Tf<$fm$$U;i#])%.m<Iq)oNY0agcMo)Z^j; %DomEF;4Ns:OB\kTYa$AaaeA:gMlH6"@LNqj'Vd#F47COtWA&2?f=QBL3o&6u"oL"=H`%@ENt+Rd2F7ChG47Kdb]q);ldJBR+fpgd %>n*cBNLbp74JGW<Ja"A="_N;cO!<.L8nJdYbesmr]q._D%>jf:-Q&/b[@[\Cl!hBa?X3hYQDlKs"V/MiOj1erSdJ?V^%$&<M'%qI %(2U`+T$#)ad6o_]8rnE\YSd2'M;@D_O;@+m<U.WNWZFq_FQ3)]Lf;J29p0@:)IZ+MaY^N/OnkNcgm0d#Z'7.9!\#C6O2>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>T<n*F[1T%Fg,NTc`%72$@skfe %=]H1G+#qU@k$G]%r)BrsJ6l/ATgKjRHd.#?o"*53"L"*5VR]?FCnmXiUMhqj;lFG1.5PRgCq"u^?u(RJ#@XbZ)&>2*!*G2eqaQF\ %EWbX"lsnf+nqGb5gdG&nU9<-16mBjt<JD._$^+4BZ),YFPMd=ElFsMO%^TPY;$.AIO?J3^2fq0;dJ2D5kl_"N-k-%)>THcPA+?*] %H:8']6f9B?=T[!=DOG_4=5iOm[r00/\=5QTh56!Yq@"c(OYlo?S_o#^gC(3WbHe-.%R!d5i1=<If4\q>et(XH&D7(+XBWI5"FT<K %<+37a5q)EO`bL.LAfCo,/\7.Dh5Z[:,\S;&TKegZ,):d)+*">^6(bmD,E9s*\n%u=F,<P*r$C)i)ZdH'q<GOMo#E*6+o1(Ad/$F% %03s)$@XM?>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"<Xe@7tc-7,Q[.1Ni:K35u=[JE^@73PSP+l.N4'_i8.\Us%T7Dad_YhW&uEIOp@3K %-`2;JE\Oi?_c-gCae.\/&t$Rm&L9,k`1&$%.aA'XmM%n8X0#Uoj(HWfBGR<a)6[CPD*C*QE,+oLSf2b%*D#!RO'CY*4A5T+g[8@, %gQ**I=JJub24OR5c$0:Pm7H>!A$oB]E-Ck[D>X^fc,%Oi?*QCC%X2,h.b<i'r:G?F9d0>'-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:k<a1,eHe\o %RFtZN\=OB:CLg_epBe@&L(M'O?c;VP6[>Ca%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<le?ZbLSW!&_/(-W[lpW]t]MqW8`ncM^[[Rii %h!Gj)Z;._kB^gP[6(]O9`o25QHsX)!3ZlRo6p:BSQ:sNULRXZ>`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</o!J2WgQ/2m5KaR> %A$fO'0iK,W/%Hdi+kr<k\Kk`EG,IT24VomY%hsT95rVi4>&@g%@nEtFP,`AW\e_m8Ho!T[j"9kYLS4fe\=9UBG/4')I>(N9)&V1N %0p`R<rqcPe4"+ju'9u#6F(gobek"Z'ArWSr5&7SrILBt1]_AM\)K?$uk+L_L(+bfcT6qGo)TX(,.ES`OQ?KbOb[IofMV5Bp6L%cO %T]D%U\"7&9hEF:!_DPFd+3A/a7\jUT>eZrQc+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.^tj<J)HI#JP=m\\.sr'ZJPF'$1QKb %R3rnSe4)dgQ(LaXS#m9bTGdGFCVHoRDXq=aN6+JL&hqtqO1A<`BlffA\#juC):J[,NV7\p#4*I8YY^c=MK:;c;.FR?O[_SS3F(-$ %"o5J_7tsJdgPV9Bl.=ADmF@,qbnr"4W8U6orShQJBR>t#_0;+c?X.G;UD_0W'qH!6XhLu>(Hki7k_WHC4,!8W,n>d0J3c;^+3lQ> %!u)0Qi,YAjGd0S\9kdtN?<i?IO@CA'A<@3QKlR5(B9ih7f8P(P'$V&W5B?:(ef;`e>.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^=><p#S5=P#>l))3+A"n?`f'FUfNQ9/]ia %/8,:=RSmQ%U[H^Q5K3nTE[O*$8V^qcn,E/RI<XYC:Fk.omPL\0_gU[uh/t`*6pudJmkQOsC>=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`KcgX<P#bLqEQC09%;qhuSC5"$^:7VU:4RQpjJ%V=G`tdO_V_136fel#*Z#;?3sA<`UR"98gQTY"ZlCK1T8c_*FN-*8(@EO7E:7 %aRmo[)h:L=(_E6P_n=*]YlWJD&W`RZWbUQA@_t3%ir,&Kqo%EtlOu*7`G':H>BoQ%TmC'8YgS+4Aq26a:a_7po<o._(1V$GE>fR> %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=]<mJY0fXe1g[/ %EkH6?9C&po>: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>c1gD<CQFYU:'\LSCXXnP:,SsBkFmdD+]CH4;pcGp^SqoYn %rm,7UT[u!9qSqt-:.,hL.:bu)g4::P%-F]BS*?`-Orrul?UiL&9[F]2ep]44Q+$oR^0&(h4)Z/GS(+a(>s)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*idjB<os%>S*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<<t2-6\J^8ae;^a5q/2d3$8FJ5%iNJa1P//3Y<@K %ESIFpm7C:?f&.]s1:-:d8E9'H]B&+6V\>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^\<Bm*N$FQG<XU(i">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)AatAJr<enn'Ks2`J.>M. %fd)Fe[jL+eRp`1V`b<b\@K,`/G@?23VXdh;L/]mG@a:e:U4L`djCAiZC"8+Z\i$\C3L3HRdAn>UlN2RAR5qnX7HS4@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%<YclLfANNiXE_aI;)sa*A-HVMrY9fXDZg, %4eu7%Ebp=:m9%E?Hh*7q=<)&)7ORm[,r0<G&TbhSCrmtFZC>i.ifo!`?OK!\ZsF+ufM]DH$dc23Xh)g5iuDHuZ&\45Y"%ZPS3&%f %Jl7tIO$;<u'm!l,\:ZKE`4-ZoZ<Je!8_YN,[F,FsD)T6<!Xl:b'1Xp]EAti\`5G9D_QC^OhO9gIRq:RIUbUfV`+>"#=`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&<ZU7g?^!C%#GJo2//O^mCdCCM7b?IfGAB))V %hq*ek:@/+((D<)ZH$7jXFhg^PN14hQ@Q=0X_BM^g(;U4O`chU5h2,,>%_S+:h.HGrZ>E<YnP"i$lfdhM:s[rsIMA8`2B$g7(aW3g %QP>`>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<A3O&FhhJ=G1rqaU6QCg,d3^-g/L.hqfb54nAekX*PWogqMGn1Sm9ge@Z/aVZ9ngifAJr.ZG_ %`<Sr<7au$[@mbW04k!Ke4DLOC;&o6e?,31:bc&@$>$T7BjK"-Mb-J6P?rUlIS<b<:R8$%?S$dh+V@!%+O]]/L\?G9fd<_'`[CtX? %TkN4U$]V/Ogu<ja95QPSX(E"T!H+TcK"9Za\VbN,a`!,;PInkf<'>C#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*B<UDF1RM%]Yq4<'5I %4aO-0SJ^:OO*;AqgN\c)0Tb;6s/LIIl[gtqa>9=/P&H>Q3-s[lj;<,h$VH<Hg1%E\H:Y'FGAB4r3^EQ#Cq^nnLIubBM7i6:1l2Be %-%fK+6V"``#\PlD\5kqqHrWai899]B+Kd$;kjD,DZ*U\]:FE3CYdX3c[bt(_+VM?$]L>m2ilo#9i`G;Y;oiP(H&XBiY<503>I93; %ZE\MW,h]XTY"X))T9'kXcYc=jf,;muho3X5=hC2T@:3+\!Y`'bk<j@eMJ:q+IE54m4P3@sjKKg?@oqi(nH!Eg`!e!6e.:!"Btld& %0:q2"1AkQfMNX?%'^.NbEf[QMKpm3/?YcIU7U?E4P(]?mR*honfL<ZNS(8D]o*m8Ek[59jm6Y%16Cj=(-ob%15pU/"<\Oe!Qn=EM %Up_"m(k0It9KEfWOqNJ3.m.W*T1E"'b,l]ll.IqO[M`Ok,*JF0&f+j[[1R.-@e<]9X.WSHSZC^n6Nsbo0uFnkh7mIUJa>u2,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_@_+eu<rFk2bCdbro[>klj/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^VF<KWG,XpH;eX4(N3anCs3!\*?C6^Xj4a3uF`M2.&9a.=QA+\E9 %K)Tn)a%.+'l[nU-2$e7Goa_NYfa'iE24!+MYe%LO]r>BS*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+;`O<fIjO1pkd&H0@U'?2A_L<`C^>W5iN5-?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"<N %N4T>%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.!<g$qGrO$'":H*e?s9XkWCes(IA,!U]S@j!Q-b-%A.eYM2Z=k %P#qX)N!YATesn[pHctmY+Za+/`]UBG=`"[HHfg&tN,A-+Pm^30>H\OUF[4Z;ZRin%W+8Qo5c>V5Z!bn\%SdWRCupL?>CB1Q1<fPt %Yo1*6\>LfoCZ4oE: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-V9I<ur!@k[K/CCR,+,]jdd<SoTu3#=_KNN=Ki33O)E,>sZD[fF %\Des1boq:?O/mHf/c4497oZN<kOiPMkqrhR[8cjJ(a!G8la<De,ojF3XV\MJD08]8i'H+]'7*<qlAa[pU>YBo7m$a*29'F5\N:-g %;eNp"/>/;Da#/OA'X"]H(_[K<<PZ1M;[O-Qa`;hg<C[mbU,a_"CqmLd4j&qiM0-DiWBOS#d%,M"Ld3fr[,##42k3Y!%_+8c\u9h9 %i)pf/?9S!-qoMcr6<rD9MesO5Jgg`cKp_R"R[N,>'dL?^^S")^59md6FmgU)B"IQ&*tf))?;Xt/(?rXgA.ViX@joAUCirj;<It7' %*=49Afu!W2-'K!>U/U4V-M:KL[/2@<P=$t4]9P_K>H):1IS$8(P##0r%dt,K'Y_WrOa1\AAJmU5LI!8u@trO2N![.M1Pae`Zu&W1 %^#8Dd/Op`!&LW\u*6n^k(!6k1feR^O)bitd<PQW]*HX!Cd/qufRP7&tNH4\;V^O5?P8CX'a;t`VBq>W9OI9: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<u1@HG2bT;6'MYL/]-H;oumL-[_'npWJ(_^ejt`ji%k)EZ"k:%!]ZV)#4S"ICMeW-Dif>%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^<msUR0K/S#gEKH>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]<A#e&#YfP4$OV`/)]"]O&W;b %m!;.T%\UUXh(DN&c)m[jr46+6`\nVsiipQjjUZ*Yb^JJIY-S9Nd=>n]m;EQ]P7[K4!s]=s_?[FM\]fd&,*G<g3)ra('jX(0>Y/.Y %HV=(WT(9<Y*^c:(GOh:jjs1*A.?ZcNFGGs^fT+XT^jAracH'N.%irhS_1X(9pjisSWR8+DL@!*g3hfO8_&T,P<u/Vp!ReXgD3%nJ %#nbXfKp+`ol>\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<S)3lUgECPe+An$O1hQ,(gb2TJJOW?5P?HCX/dpG;t2ol[BfIj5=K@``ml`UWd^B;SJ4LQp2V9Pk6U3*Xlt=BE!#X[![=2Y %G!WE)l[krjFm\.XmiE_h@jWHZ`YLj%UjMDG7BM3i$gRTBB2IhVD59jRZRh]U8T8o?FD;,,;9G`9[bq/I0[gIO@ZT5f3Y_YndW23V %iPh07)-Gse7h=6n'j7o[/tK+u3FDS<KAZDT]AL_ZY>"EFUjB'gF>7rPm3')nN4)<bqLpqTTgg4h:gg(Um=Sf/(>+>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'fSR<L9Z_lNlLcc`dp'MXS^c+W&aapIS4jJ<HX3[FK= %h9Phi5>OY?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)<l&E"s(cJ3o4,`::(_2gG`/%Q<qqHWb:"k/B7OSIgY]NMedaHlMY"Z_3W>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@Ln<dK'2BJO"EqT:1jE+hGQOS6%B-;qm%0 %=,jBJ-hE:-WLl(hf2^lP)gQf]g3u1ZEPVXop"WhX.(e;^HGPfQ&mjYuSZol/0q+bc'>nieXcsR_NOno9==])k1ai<8I>gb^+%=(` %?cf=)'^)rA&g`(qe8ZZ.Z`oTn[]3dPKnRd@mj6`<:bj+%.LdNcIFk;`"Y,9[mFFo9<sN,1O4)Aq/ul>AY.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<N4_CH4\o0mq#Xl)Hqr>&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"5oiK3iI<a2A\?8_*_3e^CHe'k4:? %WN!88#EeA<"'G<JNTF@:j%hk_[C3C^B]#Bckp@.A=6Fb`!o9lt\Aj%p/XBhG`f"IC*//G`+0_m(/m](uK2nL/rp8oh2]4VK7>a.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`YY<Z2opp^B;]Qm"43ZAWFm4XUHQ?c<u]VlrHZZVL>A,]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?q<U%40)ZiW$^6"*OjOmp('h6X#3G_Oi$h[0oSbp:]p/MjfCs$M`\cK5Sfq%` %h,L$Wf77_ifXss1@:`3a73bU;k)5c!aKCb4c('.M$]"6Y-^F4BM1HE<SE'qiD]:<!5G<dq]*!I3NB^XBnq,eY8^tRlB!UZ"XBeVR %@\'J\Y8#7L-Ud,VQd%*!&0i#RmCgiCBg:7=fubuTb]dgP8768D^j7.R"_hhD6KqgU;*ibbg$B="%'I$B)kB8TZ-llI=lX:59gPEn %?E(m_UGlA.J9Pm:Ue68pjlhSJKXIQ@s(i*[ge>rG=^<XQhMd_c!39(mMqJuemAXUB'OeD0+gmtbi$-r=brB>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;gfVDO<BUr8/]E]"X.5kfB:ZTCRKtK.qa?*L9>U5qN@Hl%a/+f96NI2PCp9"L?qu"i2I#r$mJ_WX*57oOd#50>T81UoS %GM5d5Tj*YOm!](M-5uE;$D3HgHm'6C0\,^.pUQF_J<Rt%]Y5#-%s^3i4VqW3ZpK5Z6[($C!TGJd_l0JEgeb&R+DGVkFXuf%3*@QS %OH#^H!K#VWI):N".D2SV+ra3(2UI?QW74_#cECYWnBlD]ZtNBVHM,WhjU>O5>&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-t<l!6(\$a@9P]F[YapYt`U5]FX":O)=g/Mu_^G&A\8NNZ%SDk%K %*n/_h1*IY'_CBXaG/\p$K3+B5$'Y1P['mLY:QL^>oP\:u`G@B"F`"8.jGM&SKJTOn6c1eBY0Fjkp?+lJ;/9"8,E6P&X:'laapfU2 %SXi6X`LtKn_38<eA<Z]YWY*oA1+ifceK7F/BXifd$Y=L$CmnIfBbf'PdbKN<?npO2425/S)L<QENsOcP7r0dY8W`I8:3HV9/9%N0 %eF%p_U\*B&b#f*Df3JD0BlY42iDf:Pb1a)Yg.K9j=/X.Y>76ZpaISTVUJ[4@itSO%"ENS!ac9FE1I?DG6>O^65/Y;H6hAo3qK0VC %L<f5t!p4iXj_hS4f%;FrB^l.X2'Qsn7Elp=PWUVkZB0;4#Bnckfkf5nA3?!KF.%P\I&+#*45-U'q<Ku!@n\Q"X_[tG"2f8j[L:aH %-Hk]99,a;"8p="f2aof6%ga[,b8]S?6N'@t"_&YK>r_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%,SXNQjkmL<D,B-!*\I>0Gj]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<iR\tCmdtj:mFHeVeo(.Ps+U<C:L@-&W(+]If*5;pt1J#fcHkU %T/)D]*aCeW^`\$[M94O3,mCtI8_q)mqOV"aZ[+P:F'm=ODuB$4@0uUG(\CKPTd*fL"h13\<1NP2;%[LnGhM0](FB77#OYVfgXS5J %,78.Aqm/do\(248qhl/6JT\)85XlSjA?OF^NY:$a<Xp^3\;Ue1KmY($A/.C>\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$^qR4bH<l%RR6"W=p%1kJU+;NNWY!&a:f#(JG?6elL6ke17+Nji@j>R!/>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"bVbIN<!lFVhI-n]bhru[2hjUSM:8qZ2:86uAM7\Lqs#GT^4)/]94h!X76b-0B[jo!eHN":"gM)X %G)t;imT,8U)K?BV\S(_Hb>Zo23B#=0GFk)44.<drRQZ/R5N@^DKY>Kdl[5p6W->0bEUOoiPHGan9Tn[$p&KHcGK+Ad5B_qNQ>ZFq %,a4p"/@_8rcL"*9B"Fm<b^EU@K41E[`mjIc$Q_@&"#l(K;k(Q/n7=foQ@9g-d4R;OiU%CBAC-.S<_c;s`TUoY68J["\hdXsn5$Ho %'DkduS(.\K#*gqPAlfe-*`0GraFI,N&)_-0$[:(@cRfQcIs9)NL!8Em]CV*kO*8Lhh/-Y$h,qSE(CcVeDsa]%$OiCOA_"*3@ph!j %Y]*O^4"!BgTt=BS\p#euNt/QZ=UG1Z=<B\K2]ZMbAcmp1#.O[%*p[PgQ:T'5XZQpo8$d<s?nI=X;Z*HHPC+/rCeJ99WAj[L-60$` %25n#U(GC:1oSGVL'u_@Ll\T5A%'``K;lXat:#N6lf>p%=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<DS'8^6GPD0BS(QP,!jbnl.W!WR(df07 %"4;.r,9\&&]h>^BUl/RQ2$H+$>hJrbWL:t$cmt0elkP\/;Y-U@-05Ol=$i)EZ0\_H6m.b,+aYc`6uAa$U:=LJ8o:P-X1MZV$^FCV %)Gr:f!%n)A.mXpPCKlqDiIrhq<siqW3pUlFSfTu02:_qo-CM@Y@anr"@\k#UN7,idclnLk>f1/SCS`++Y'?<Yh:q)9+cW4h##CC2 %0tn-E\%pG[S/NfRgFfoS3>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<d7> %B_8SeOaUp<7LFY'jt&O=JhOg6>/t<D(GsGjTAho7jV2>hh+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+><Z=oh-gTTcPp.Rlr_if< %K-V>Q]/21];inL@\4/A/A^/lsT*gYeGS)Lr<a!-rimQY_<mn.1)a1g?2o6SZrmk,DJ#XB=hH;9]/<(c"r_c-4Q;pe-jK7',k!tfn %L%?`O80378mk3N.:$OMb.'++WpTQ9c%uH$a\Q]kXEl=1:TjI4l!9:947c6b1/)N4?Rg_Ym/<"Ube8p`[QA)Ojl0+IkEmj.RXHhD@ %]F3$08B0\J[Q!O/aGP`1^QsC2O/>O%`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(Qbd<Vr,iW382/+KPg3s(f,dj^)b1jKV%r,Tb^Q"LaWg:>U"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%<hPU/W2_+bB[C!?jB?/_<:Wks1P>Z5[lQj*82cpj,:)>5[V%p_CV %.eB--7:n@d^>-g=]"X`=Xdg#k>)<rZ&4^dj2I24nljh3,)r&$>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\<cuSccDW^`-l*T8M+A-N"bpQcp^9@8!E94uAR/*hg22mYA;7B?3 %(!F*@jZ`[qg,>+XT4<S8/=9<uhb^aB0b^-UIDITdGQJS]'&c5Z/^HgVG3*N#Y(@!k3s;7U35!%-:@^j3Wot4>,>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(B<qJs'@>h(bS8kIbCGlS%24^iWUfg[nFKb'9/4C<%S<ji?#-D>@S.'$LkCjSJ.^.VE[H@Kt %^G*,)[j]gNhln`p;lmBCTTOB0E@5(!LU%m(GR[D*.?0Nd90&sR#<l;(RHZh$<hi@pj!rog#,IDun#QpKd[sg`I7*=Bn]Z=+m?<ak %9okO@r]:iYikj-5Uh\p73b?WjG'`l$/R&7o[Ia2pTZV<@Ji+<R.FQ5Tj:gc"mNoTYAS*$^fX@?.$!Ibq.\q"&U3btabM!m&.upoV %5%PB3O;ZH`$#sPH=@5l2=!:E,>=IH#l9Gn`0BAZFG.Q<bBSX"?+G+103d.B8V6On`=)rL-PW=J^he_(<o9Kg3Y$=W43qq7uAI>qW %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*</2$mu)[>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^"<GbD?8rFAol]#\pJpibNHTdn\:)cO6aci6REiTWZoNn1O8'1T3?I);0i.67\l][TGcctnis[q=bu%<kB/SQ %j1O\DiioIZ/amk*G(+H^eb(fGn`e<%J,fIk?bcWibpP/OIXZeH_o(rWq>O$tGFNUBk4<MP:]J$C!dhQT:G4!lG\eRRQ!\AYk__l[ %3,R6kB<dj8;S(-#=Nlf7@H5&8HZmD"^XiU2H9CG7CZH."#@"P$6VsNN?<-fdP3>(YfVhU(:V5MrhJ;HMEX_AC]_nNXZ'Tr(7duUk %IfKDjLUUXHr;Z,s#IIN#Q8\Yihh:Eh)5jCE=1>;K^jn0iG]HW1,Y:`@6'0<E!@/#=$[cJ1K.N2H]SJ%7d3TD."9fP`)$ORPP*:Pf %LARTKHgdsqkC#!gZuu6B<hE^!^!qC(SZ4^cG1D'8o/(7?W(DoEdG/..V`"\J^Z+ZnpOQETlu1gSL%ugbT_&T,8<mk:?fS*JQ7-`[ %*`Gn;rt"7iNaac$^0gLSpj13'^U=%NHfRr/"&Bl6b"Y@$Z2d\N.#5bP-G`V@W&^G?&],a>il"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#3E</WqDI8fZnN_aIF@\V1YEB43OB[U?Ks3"$?2$e9g'cu9;*l_Q&H$K6:6 %p]tSL8ZHInfb2aG#^HeY:"9\+hraD;d'I0#93^h54W4&"]JEMB0.-W'dQPZl&h:<n@.?WU'8(rlO]YZ"-Ih6A*KI67KEXbD$Lpu, %R/=r`LP[nj_F'Ka;d$UIYn8I)OY:1`aIX&8ig<7VjKRTbZqh&&03W\UUu8O+/dr<RM2A#S"WLUhb:RD>6i')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 %Rgk<JP!<Wl/I*T!%:n_#NJ3-SIYKt@"Ct8Xg7^up9c]O3P*%V(Y=kCuM=>OVA-*3"YAjWd6<Ao[+%#?":(3]c7+01Pb6"SJp`\]R %LBIhe=_1&V_l3ku&j^efcXRO%d1^9&l6K(l[m2.0'V]&i:Pl)#"=$'EI[d906G"1hF)Xj/:Y.-1>r>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@c3<JKuHg=A;P_W;oKV`b%Ktk(.524q!)LU3k?&/Xbh'//WPiF"p+]Rj8uKce<pJVI!u4 %.:tQ:cRXTX$pr\\f+*_6<A7es<uQ3P$9nfhQ<]#$&Oir6Pf)Ftd-!eq%gK:kOOlU\DQr3tD9)fK*5pm:@r*;'i0tjF/\3-HLGd;$ %FVZ@/]FsOG3LgSIAe8>F_%2.ONQOWhh%(bB8D"T-h>hs`b5)+-2/n/WSH:c'0+?:2)Xp-<P"i96K_hk?`iUP+V]k)(_R9MUQ>@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`<ZZ3<s_k\EVpLTk%fU[QMAZ.&oBf=imSo=jS1_%,Uene.WoJOc2fA':nB=s7$EH2[*pH$J^8RTil/id %n&A;/`1:M=8:<7`814$&OBPBYJQ_EB0_%aG]jC5,$f+%LbQc*?bFhJX(,#/el0UrVoNJk()nTi6<aWb,;)r&9&0086Pm)YO7l&n9 %IDS;3fiSadK0FC!%:24:LEULLLilC1()X&iPMCbLn-#!YFo&mSJtm@2_"K+:I@CEScsjT?Ua_'?i;?9:KgeS7r%,lD36!cY9f;Km %2::DDKr,,8o2jB]9dg%aS#Em?-^mq#/V8!E'9(e20U&@q&SU7i9W'^-.`kn3)MNUA03KuCdM8;6ROXVnU02jj2OOraHAC)SRejip %bd#*kKgLu?:eM-Ti6?ccB`[c<L5fH?;"-0'X.UgD>YJHBBb_=;'cMFtO)lo0c.nUKZQTZi/Pe!OY0.`:Tc[W49e$=9F$_FK@NCh8 %=4dVRYY!91RT8@PQmNL?-F+ae2:$S>)ogKcC8J<_e5]e_G.<BW'L,GtO<l37J0(&/CbC6qKGN1"$oB%4BpFNFesrTAVX-KR6-5@V %.*fgHH+("-=]d:gI0rtg$6khFWmH`_=j1F0!rgl=/7BOC612RV`@M^6[AK&Z24PbO'To]>!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=n<c"DgdRCgMg+l/kF,3&*,e,&ld4AV` %ntO1:DP:b'BmToF&PbZpal/%nK):+#^lrC<Zq`u1]W\mdfO5Q,<?\(@0e%[4)t6kWMSOq?:59#2,]sUV0rl)f5]N7P0Si`;9oBO< %"PsX;4@@04C/X'49WAST6m3P.iM8#2%5:'?I"kksaODQq3"(Qe<>It5,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`<U7 %CqQ'Q`9RS`p>?&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<W0Ck!23>&mGYG5gVDc$E9\8[1obeYq]0*#]1NYn.OH0:SB9OgI$\6co.'p[@qG!'uhK'8QCQXD+gq6,MM9] %+\`SRAVEe)2V&5OU2@^]hBfF8=6LK<H`qBFl5W'?DA%^/$D=.h(_i-'4,?_CT>\!g64%@]*"#K;*JZ'8;<%(5@?n3p(56L$B/0eq %PrQS,!)r0m7tXjAK-:\['gG19?sDpC8HX4:j!b9=M]ZME^bKuuV*W>T!LX-_dgjiKn<uIaUI=,+7b:c/5&lr[el4!#X,JjQQdXQ[ %3-T20p=]X]cK)SnU?g=>,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<rG[7Mn\1;+EqY4_EYcKp_Li`+qqqM+fMV=rUZM*MlF%/q[O=GXO4TL$/p\Vu3!> %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(=\7U<K`*fSQ.,&m=41WfSWG[$>2Q7/$`6V%/T&U<mL#O38YupP=lAMp(+nSJ!I#<Gd$,))a?_F] %/or>W_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<Sb#A0na;MVh.@6jEq'cKg7Cnd"(A#TOD]D;aD7J@Gd3:ceasR:pn-TTeD]!Ylan#E$ZY$)jIA %@&5,R5c'S>#bdP",iG\ZMNkb,)%,WNff)0bf%0DIB\R+Y%jB]ZYfF'q:rJ[S3Su-)B5DVtXDepu:Mk+hLb-Cs/INT3C?Z:5%I2*4 %J8T2!K45K/d<i7Q3YPU1^7/$"1^o.)/s>kMPB^a0k&IK&d'U\Ol8GHAQ/!`>0sQ0$#lnKQ12%&`mfY,]dZsm*A9AFujB45GO'X5' %6BfsHX!iuV64Nde@TkA"`L6eEU>#mG:UWB[&-SMtKT;`rS"hEb=TgM6ggE[<jMX\YaQdCi$;W04'2_^/ZS48,DOA4YE@oYtZ\jr$ %RT(h-WY&nU>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_!H4<ak6bp?6Z"M348dCj4Na"W>Sm<9/+Ltg7_>3Ii&j*'P&/'\\D&KKHoFAlT2#^#<M`+9dS53"7Y*BPgL+O#F: %q;AX-obbngE\+0Ln;Y<]duIN>Gr*oF5RsL23EYer<j%0`7J8#QWsIn`TpNjA,=2Z`V8_dA69I9AJ3GPEH>R,1<N^Lqqml>g!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'>><B7pm2GX8[Ke(\"I=9)%"K=%bF!2`kII*3^a.9m]!OIR\GR9:ca`[Qp=QEms%A3J;K@t:a+iC^M''0008O!`8m %nS4k\.\Du6H#)5>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-<H)dY*EF'9o2n`$TgHL#E6='*fQS9Nn6F^M-aWk)RX+8Z$rj1Ts&6mok9PnHkV:Dd%7q&Bc7pO*Dra!+T##Q_o0E_$6:jfUT_' %<2:^a/eL/O3oMVA$g'Dq'b80;L(ndT#HP']==d-]kYWiRcrXEGK+5DBS:upLVIS#t882)I^ges"0iP#SZ7o%0@c(-'A1//4e"2.g %4"PZOnbrl<GKW_(K":AZd[(%*BL9a7U?Mn3+!Qup+U^(W&3JiGYDB2)JW7d4Tt)WYDG96QI/5MS`f.4c,F96@gP`tVIAs;[=Y^)D %Dj1NE6h1C_L(a2P_d018W"URr$/O2&gp*kjY.*m.[8+n.00j\AE*MoA@>.f31bo+IbV#,jpn#FKLd[<V:.RK##2e"1G."a`D%]a/ %:g_?+"-9h#`B,r$-Pq!2W`+!%F&Z\3lD>'>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;oo7W<A/^s5p!HBk-EpkuHJ@Cbg9d4h$_Ws7/+J;.i4ApAgR.u\pYFCAn`cS";D'.cI %G1pVc/-;-jM-!'JU.mAB!hAANY%mQK!$F')ITH>GJQ$X?hUIf][nml>Z]VC&OD^AKG4Q-+ZZ$Q3@tAaNpPa^FM1BK(Pd<_H:]S)3 %4drH@VS`lMk0kTplqi;'/VYd2/f0s*Vo;42A+k"b+?Yl2oUnk<GOc;65RN>s4\s";![$fS1(/f3p(ZpU<fp)TJ7L5Xh*hD'W'_`! %])j@.4Q[1i$MZ;^Ql30/$^Up]>GlrEq9ViSep%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>$IkP4DtYKY<H,WJ0k4<S-cBl!/Is`ClHQ&>H"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 %<BpqG,*aMi0a+\'RN_+,M%u=P5"),:MN)F8Lf^R#H;4>j%@aEI<D(K=L?%B1k]gI:DHuSDD78?%mG#'eRbbVj`ssi4?fq5Co1A?X %)Q"3kD:>B`9%Ra/&a`glQZjR@7#ZcFN-_2@i\ViQ.H`nE-Dhgj.tNJEX/8Vj84Z-WF48*Y*\fec(lR<i2$6$f(*7(N@?ub%+e7'B %R5[dKe/Ya=@^6OR(Pfsf+pF[AJ`DqPKa1g3/?8s(5;]0LisTnl+f^-E0k1A<_p6H(M<scR1`Q#N%4[tNl)e12U*M!^L#W7]N^MQs %GS:`V<?t$Q>(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<JAas>^TZtO?!UtZgW1FO@ %$4\.8m?!!`7J)(<g5cKGOUu=Gn^`:,Lei8#mG*iM3,Rp<J?J@gdfrD%a_uul_IPqXKbb\&I!$Q:,5gGH[d^P(e4e5e$W"GBdLL/r %9[TT\j?f5m1)Z5%-[.VO!7@HPPpm<h/D/2e'1EPD!K#=O2PaHJ(heL`cCuaKLC6ad!!Pf`?'@Ui&.[QmVccj$$!`7"Ytq/L7]1uc %Y:2UNJ0/eCMW5N#L]\)UON=[-cT;tf%Bf'E@dHap99^>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$h8<M,54>c7,`(%a?qtB^-'f2fk=h#'50Ip*],['&M#'<[O'2QLb_bu.<<i:& %V/B9`O*7RcJ;QBm%Wr4F[):Nqk_A;l('@#/eeT#VRN?L9M80LOhdqCGP\:+k#)@d=*t&%Z+Yj_hS[=;p/HS1H[Nn6(5gLX>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?)<Q.opN=4=7Zm@-N*I89,j1R@+ts(^8d<M`1<m9=ZJ2JU@njiUEeWrI:tp9!j4 %Z;)VX[Ro5n9cFd"(H.'V:bB:PX/6?UCGd%HCsQ9Z&hReqKGJ*mHr5BS/EAp0oB.V]lHiH,AJTO)7,,/P$5BMmU.rh.9sB/",p,\K %,D4]e:qB)rVD%F'KQ6rtNB0HSHB],W,cX;$kc[%?PQ=uP;4$J[M=](JRdn`4&=!q1$M=andH<<\[)fUlIW&#>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/k4ql<VKC3:U]AUNB]ci!^E %MEY+FG:&?oH0#ie<U0DV4\(JPdcSN@od+-.'s?#&fVnd$IbkS7&-!dOo5otP"5iOq`;*QnohYT5rprCCQG!KEDe/mLn+nop1/@dC %]fJ*^_n5mT<pG+Js-<@tTrK\+Ff=d.SF?=!K+IpmpuB]>lEtNYT_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<ShOG)&9c<F;q8-L]X^ %fDkQj5Q4[6/!VS!J*sd$iMeST)V-P:BO:H5dr0a1@r(dN]1gai'gaM<qd6GXUfj6$"nI!!hsUNn^](`E3G#8AXBMq!n$KH7.Q;4r %[UIk%f#qG!l-W#is0j*AWcHX*<@_q+]#((41sY@=jc)l.jmg^455OVe:YkW>.=,!Rg,7%,M0Zt:ds9<prB&NMX+NP(r!P.nq9tCM %<B96gM=a:fG8relb30mhT$R>iDkA[[rY>1!bb%,QAZKHi<@V"Uh5GTl(/5n#U0-Q=p2oPf[B"uTn?UpjV=Sq3b%a+dn_,Ws0,g<Y %o@Zfu(+e(1H'@N%L*uT&Sga+Y]]t%V5<[S:eAke.q<"a>e,-eDWJ<TB^1hc2(9clT@"$se.^a&XG;GSfb-4\9SiCfXlSZ#[^RAT_ %pZGW7kl+W7VPWidYs18aCY,TIAZAaPPomU.^kM)Uc[<'66G"qhmmUIR)`Fp1omCW_$^H.RIZ9]bRa90j^&%VA\[g<fDI;&2qSVR4 %QYc]q\5BpXjNe*XTNLT_L)'<M@ET0cHgG?^DXPLBb==E^s"'5\kV787KRn%Us*]@Q<MXr!edY:6d=S>sKb<WGgkss2jj7XMZdMoM %,AZ5e3R`_k/s2cM4]Jk8ViHag4FaOkqTI?s`Of01XA^8/kHN4Pg$d0&g*5to[*,W0[9q6AD!7mZgO(o*<UQ^^*tKchrpqXG-G29e %qOjqf(m5D0lnDuBi&37LGsgu`4"e=\cL$"o9DVpief-SiiIWLse]H,;.pTjXU`&2RQcSLeB<i?A^Cn6F`pk]/Aj)RUo?2'4H#N5" %@lm]7cpQ87\)k\TQcp=L3Nq=5$s7f9"uP;QO5<Ld+;Lp.J&\5%Z\CBao,Hm/lO2C05@4<1e@&C!pXY_9ktKSaDP#QEX[UsR-r?b` %q"Z!_R+S0;JNP"RhuBAJj5mFbeo'=9dE%'1:GE3#=Rc![W*5Mu//q\DmFN.Y&"ia8#BPX5O_6>CSHrQ@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<DJ5=%]CbEq %Qd6cKI)6g??<1sbMSiMjG?rl"DO]^Wn^M>;VCYlTI.;m8Q1MBJIe.pBc78A8dVgbn\p8c)MD=?Od'KieD`\p2N8(6h]Mg-HgLIF1 %Sb0b`[EI_C]#.[eIJ<bsp912R(@R7&lHOBs4s!l\\M06i]](&5lfPHP^!+E50=N&'iVH:75FU7(-'akQPPt46#IR%nZKH*i#@mGV %p4[FM8QKWX=(bpt^%)'Lf#]CSNEGc5NpZ-J%")>$#ljASQL+@,4f26GM+//m3!!g7eRKP2^D[&^jT[A&#]iK'NMF[C`p<;2otKmC %Z$65Ne^Lj3<iKff^HWK]8iPs+%p8OraRJc:2.ITiU[4CL1Uis1C?=;5\UHqohY#St?G6*SiEaVrB`mrKde64].eK7F#"RKq4+.(` %gHU)a@6_G8QfV-+k+:HbZX1;eI@':[G37G;`P"mD`7Vj?fDX#u:]J'#n#@U6+1W_%lK%)1D3W<@<:d)Y:>Nj4>s5R-[r-"Ed<OI# %KBFnYLdnm4_>eT1(?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><it9Dbs`*2EqqJnAm`4!Hc_j$Y[WAgNMoIK:Uci?`\Cbc %&$2>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\<SbVMc=V_h7c`h[HJ<B208@\=Qsl[I^>iJQjBcO %dl3o1PoI?<*oHe@W$V_^$OUq9]+orKW<R^KEjZ7'I",,"nf(lGB/_<;qF?s&d;0?H>sGW,KWmVPFu.S'Q$_oJY)c(0Ng><<_q%'l %7NZm<r%=SBf?6#iepfQ9D,(%8Z>%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#E<pFOC:62P?OOXgrK8\Q_Lg52s#FY=bPPZAj>7`/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<i:aad69r]Q-Q2e?c8$o71JsbMTH><``B)%opmm5&oq>B!G(M %,F(:u)*_P3a%E?D?Q$.1)P>FOl;rj@MDQ3aCsSM;a1leB`LtYY<9<)>E"7l1S#<a/gJr?o<7*UB:RXD?A*''[ghtdASVa+geQ%T2 %\C3qQYN9*dp7pK`3QZ"sC>%IMnX!NE\?'&EX*LDfI-r#Z#co"G:J=,no0JS<O6[P<,!B0kqsEo`#;k&0cE%3,?FbLKlc!./RaGrq %nupBYVSt(CIaC5mU;uS\=a,ftEV&7gR;Kp!0%-EM`#06c2i_*/DUr%\N#U?AZJ%8q>PR$>>N^4TCS_n.G-cCYJ+J<lZ:fQ%CCN3^ %*>a/VLVs_8k'%DLiZm!bO.AIK@+2VOp1T_]@H0lPW\5==m.'?7f5LVlD5t=O7s!4j<VnGJ2)G")p*mDf03-&1OM0]6f1UGJ#:lB/ %Elu=O9bU#.?F)p2-4XoBXb`ue2OqL(qqTbZYJtI$>)Z[6i3I[\\LljA;V@?Xi<irgk(B9#Q!\4rp;CtLZ4bp&a6N;d4EIUZ]p^s> %^:=ALNDE[E<i`2=NiQ2C]6Og%6CTPeFC!I5EHec]K9h/1Npb`=M!'mMhe0r\3CDFYWAT,,bQL.HSWMVQq!HtpF&B+Wh9\L%lhmp[ %3ar3e/I%.Tf(HH3cEVcspK)`'VjmIYG3r]g`6s$^gS@mapSD19])K)7@ct3rXs$^1hCFXaF^ZT5jZF9bkVmlc]CJOD8W?Tj[VT/, %Dd_&GCH(NAq(q@<QZgVu/buPKRq9Y$N.:)]GFRUP'=XL,Zs+om(fLl^/_[5^d'oGi%k!ra2gaS9UBe8X[U@\1pQf_\Hr1UVlF./, %dbj`^EVZaV7op3R#*nTIaT8!$Y9-n)"D?VI.sDgiUh?I`8Q`O>TC+pfjmorlhsT=,s2[3hE,H_$e%]SUcB=q]EXbE$porp3Yg;0K %NZ7]7G>!9j<Jo;n2Io@f\r!R%D6C==Jq8FClK-2jeN@q^chnc2s8-'cH@qP)d+4MPBD6d/VY`GJGFk*2?/.6F'q/'0o=i+tFKV%a %BpKjJr':)s&"C;XF#me1IAU\Gb-a;@R1Tm?rJmB1%rV:(WrqNaq0r*R&ruF9X6.&XH:WgKnZDoCb-8hI_K*"prkPVeW`=P^?*D)> %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<*<V;8KDlL0%1i:96ZS`>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(Ip<rhpRhr`ib!f'+35 %#LubN;J+\J?Q?V/EV!,3lR0.n*h!hB_r"?ThqrbXB6i14$O>8/l1;KchXtEM+\T9B*&"pLiUH!K,?gD_c0QV`LG%8+o[7*WkMO"" %k1^=Q8$l%,JI4=S\mXQl$\-^`Gl<CB4$T2jYY!6<;mAlahQtf$47h&CB@edVh6V"HQqk&IUscDtjZ6*5FQ_&0J!I%i\p")*hCk0g %lIEC4jp29kg$j]&mQ5^YVnVQlND!momp?%1`*U\\jloWi^0^9-T5=Mpo];_9[[YZ6GlLjG(Ja2MV"C3sc36j"ml+b0QMi2V?i;^_ %]7';)V.do_D=,bS_n'HF]GmTV:t\m%Aph7"c_6Qc03u8?j6!KA%>3EEY$\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"<F3DKY`aSkQDsjQkMH.B%37WY_X,7b<ioZ:reiGEglT1_r:5d=CHl%3ACe7&KDTh+=gMS:(\?*tl0f"; %QRYqBI-$g!]D!C&CrFClUGH/M7ZJ<D\6kR<2(Fn@q8-J^`1tZe,tjRT=2%"uX*j_`AR?nFrrR`sJ$Ts0PJ5JB#t0tDCi!5aKoOMk %+Dg/`EOIX6[Wo?e5j==/kPoWQ!0O`Fl5L#VQ%V&Y#cE)/hILR8V-7tSe@65`kfVtLX]i)If:l!G6uogr3DJ7@oA)'W!]3H\)ImA& %Ie_4T2hE-LM$tiY8=ibIM!>*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_8MsWW5<W&GVrpKg74=+Z3]=9NGk\+UOcpX@VOuD+*;"?4gPm8jo:E#SVQ>V6nk&T$Li^ %%O;/[i<A-0j/j&Z5V_pVjP'sq+s->/W,-nANn4X]Lc%Pocj5LG?JoLhJ28<g&H2r;0?4kkV(V8aUI74`[NH6DM8Hkq=lMjl7M(<! %QD>Q*$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,p<f@[faZF?qEg0MtK%L7=<#&7JcKR-NCk+@QLBI'n8!6Gt?Ykn/)d]!/HOZ/J/=J8GHbi*"l-NKfGQH!@gOY'4'0@ %LaT5@!r/6Y9JhVpV6VLH&9\GY-_DK)RB%A'@ouh>LhQpbhJqfQ(6cc0<//_'-s7``U*'sq/3&nck?^sVM9nd'AEJP#%6$#XJDcL+ %'K%cX>+$e[C]Yd]'<FkNpK6183[9JdR=^@(e((@M7*e6.(/Q55D5,"TEB9(CZ&->cKore!GF;O(((-_>-MeiP;W4Sfkj9[\"<:sq %-#uZ<4L8uu10BJKMFCc"9[&dC^]bS\08N]G[:tYQ9Pr9e"_DQ*jT8P[(C&>+a^>.3)KFQ5'a7.[R)FnTPsoO<W"OqL#O1SS!S#W" %AY@@Hg:_P"dGG*qa<Asi7HG/.0`$/67Uo`*YbDFkA7:6];bT8,3XhZ$@Y-N^f+QUl+IX;nW<mO,F^:<RJK)j!FDobrDJX^W7lhAn %R?3X@E4.bTWo/-GhupS'gQb.3B?WA*QAKH.<lQsPkc[=:;qM)..3OKd',Zf^/$qVgP;<X]^,Uo`P\>64*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><s0@GFh5'Q;NKrXP<mf=e=Y;23RRSGnh-^pJ'akGV:r=-Rn>hlQVRRJL6F%C#"#Pcb:>+LYJ32p %]q\1M^it$7meDqm[<Qbj78#m3^/QW/TW73*?tp9PeN*uOhFijok8hiLYaC10/(:ne5jfA$d&)8`<@M*Cj]6*2PTfh<bI)c)666Y^ %FVNK,qNth9%4rq+2!SX2aW/902L8<q]COu?m9A1pro#eR=$I"`_n3MRQ:%h(qQh!Io+TLrXYPVf]f(*`K+oi[3LZGL\#9/'6CUH4 %?IJ&.GO!YL/QQ6(c2FN+r5ij11'@,;J[SLS1nh\Agi+JsG@,:]A+_`oR(MQs5%F#MoOl@nYN=p7n"Y?4f"#XPfrk/+V8K9"<*jf- %2tu(9e8M`!oZ[p]f4<a<=cMY66/Uhe*ZUi%C\5oN3pBGiS$dER-i,oOlcn5FUXa;Mk=c[7]ALPu-o$,BkHbH0;.[lFgri>: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.%Kq<Jq*`%b^9Ndh`*A(ri#iGpgl*2Jae@'Rr:d")[OR3PZ)u6?T7I^m\Pu=@9$n4 %&`L7qZ;$QCpcr`b[Ip869@kW4%hpK>7eiqB\[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<iO,l=,EG.YuRJP?pij59.@P0\fZPnB_C3JDYeU %!2Y&/9"JGSpIS@iF>!=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!a<l %2Q#XFWNJ>A`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%Yo<r=\mIUY3G068[Cj^rdaCDd&lc*MLX52Qsbtli[]kKpMeF[fr:F*)14@uDBc.IAPpX6s' %HQ`(",2$.PpT#4iiOZR+g8=iBhu.`.[WU^[qL4pK-['Xq-O(R7h\1@t&jI_V@5C-qM]Ug&Sc<nUr$n>cMr"<Ld!Sk=G:1qRg<t@8 %Ec,fle[sRrI(nU_)R2].b07t?9B@4eMnPq67q_Gf^l<2AUX`Rj14N)iS530oM0F.+$fZL.$e_ReS%^6l^mf+$a/YQ$k-Stih!K[Y %HRP$\YGs`M\dPb0g7BaM%;>]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/=RUK<k,\96@]AT2e#tpUl-IeYY %E"fSnKV"nS.d$jYPNEjp29GY!r^E&K!.oV`@/Wq)I?L1W%fMQ\!D<!1*m-jDj2VFZ[\)`UUC[se6W^Y'#AQqe/,`>JI$o((=XZUg %/PBt]E,pZ6=t0B&HseVG$d`fZ$5Z%`'0i6n,kN)%eX#39inLb+c:>Dc<f]\AU1ZQ`@k2q.QtR[L5:D)6OFh.u!'ZuAWb6lQT@(HG %-5+Gd!dFJhdfP+%Th@S).+pn3"=RB&r[:<Z!qJQ2C]U>!6D"qqUMaJ]$quH<G(_se'asta500MolmfuX#j;?S;iF>)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<<K1rY` %a#Z@Y(m%NoW'ul58%PmXqhd0@8iFP+7uG<\!$OAo9U%pKFYhVmfSmTLi8[[G/9[3)(_-Ot@o<+X$j3:Y!i\;CWiVR]^-(W;Uj<f, %5-IECA)T5]6m'rB6?$V[!FYFc#juQHpkY7CpG[mX=k!EupHT9u2HeU&Ld;t$qOaU#MrWOnaC:eJ(+)Lh%M0I#6m53dCl<5bUc-1e %IGlbHI#RX<$O'aVM?bPVaR9XU7bnp2!)^3ncjm#%=9ct5=LN0;JmM:;-kM,e5SWG!fN',sl6EA2PbL_?1uQJ=,g\kL_P('IG"e8B %9h+M<%:E,/06Le/ZjOE!cN#*d6Y3Z9>=/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'0oa2CFb<aNsnOX^(7"(aND=Vl\4fSL\[6!sUS:L6BV'7=8.2X(Z8Ia/s9em4^dMOiRT^7`d;"SUMYLKB?8C5rm %UPH)f+="e]+L3Oh-=&UXYugO;!?PC-5E'gq(E3pki='NQ+J#FoF)`kk>W&M?BU(8V=p(O7;\PP2?jdu\K>3aA,0d!JRg#"0D'#e` %0F*CE"pPR>idgJQ`=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$O<KBa[$*skIb9S3,'[3B8p.Pc.,u#iPgnqpIT@*YV`"+A;H!.4AL<(:cXO+B['YMf4 %L&D+\)ckPa/D@*PL`,h*PNC\UW"cti73EB<B\'p"/>Z"FhI=]K,8qhg*Cu1XW1T/T=C_N<kcI+nfrXTL8?n<\)7IY[M&Od;Y5i_s %mKiro*=9WP2")XC%+O6.P1gZZFA'\PPZ]j)g4\eQ]chc#'Ba68a.0(pPYBNR6:ogNk["Y+G-HEe#qn#;To?'P??DI/k"4UN"c5O7 %bV-tiJk28;P4<4<D'3TO]"H]&e/f%0FMQ&@@Maj(,0`)tEeS<D64$><[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<m1kbFCPifm%<0BG>(i,iG %]S*m<O*aq79u8.mME/tU;9oCO:aatJC"B!PH`hU[iup`('-k[m4RX'U[.;ZIm+B_jM&JW9;3Y`cL`ODB(nsogM'`X\6^$YS%,RFA %SOf\>8!R+!LTk*X?"le42;'[=W=^^GgW-c=(>sZs@j@M)<>Ia9P);Z4jRQi9:hiOEPXr-=:chHfq16*<LoJIU$j#6c`h>)mU1@C) %*,.q&,%JMS%)2dm`=]r/M7bdAYH?Ugj51K=.pIk7_CeMa]XEIU<I?tsI5"9*!R8u&FEEa]8>;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 %<gs^2cer&R/)d3C^tjMbIUI#7H-CrOR/t!6O%-$CnFWY.*E,]9DTE8X_oMhqe>7#S*!Lkgk9qn//bfF(>nQ?8@hb,O4WP5)T>IDW %+lq\@^dgE<P!"cPfRl=I[3*U&G^'I+@X*>Q3jI_.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> %(%<m[I+G>E>A/0=o5q,K6GSK#)>F[HkQR9I,#UfDnYD,1ZY2I*+b_P'G3DgJMVX3>,-&B%RrQeqQLa^?m?("&*B[_i/tLI)TAS`) %$=CfB_8:'CW!"-H6oH==$I9<I*0.m^gjrYR/Nt_@j$=KRLQ/[.)8QJ$=!@`uqKs4913,gPWT*E%$g#aZ&Bu0.Zj,GF6*sV`]Jp/5 %(81I-Nr<Xt!hhgm?0L<<JB\t;hgHF(QtTt7J-0CJ<9!:iV?r%:C_;D@L*3?:39$gaK[=49+GAE.;QATiK0]dKJJFX0I]gd=&<Si7 %.isL7V.G)<d-&2$T.Fh3!LXG%&Tph$#6R`^o>5UqB3%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*&,lcb<IW/W %`a8[TXrj>sQ$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/<?^/e;T6B'n3Da.TL96]ZG(1E^"o]go/Qn*B8obLR/M>fsJ8E %p>W#$^JM\J*jbIZ9.rI1&T8%hF=s#.YRjR.O/;\jerZ*YGL("H+8Fk<U4-CJ=WO%`nsPi;8(JXZhO%nEW_b+TK2n]:nF[^4p$CDH %9[!Fro1qa#7#a-86$CS!0J"-'TA2T@n+gFSUY2kMqtBJ!;UEM@P<%dFY9B^SMfp,r8C+(Cb^WsnM8K?dD\iWopK)_8X.QCFESWSJ %^Rp/QL37k8&1)!Ej8B!>lgXL!7p/n218RdoW3!sEN/H_@*^?Z2.b/SfTe?r$mu#OJIa6H^Z^OC6e=O!lo.3&q`eG"1Do/Sl@!@&; %-GsSn(=$_/Q%qW>QW>q<Gp9KQ[4*'hD&ee?hW(C059+%e;Tm")[F3O'<qAQ?QR^jb\nLVgZe$g`!1'kq\NMYjlG+U--6F;>qSS?( %.s@JoSU5KHNg@eNj^0O)lG)T<cc8`uMKGT;5MWRKJh:SDUR[pG8d-DHWl9i;J.DF5OLa&HKb[$pC#$,Rg,%>6ID98H#1Kq^FlLo: %n\Z^@_)Aj<I;%pn,"A<sVcqt<2HPeCrH5-ocqfZgnqG=o0pB!]F0?WL/9uUrIe@"XU$PTfSeb`(_t#-0O5JieHD/q_/GS1dWDp7o %2I2T`G+\Q:#7f#k7o\KE[4:%=5_3UUbs^XjeP3rf)`jr*M\<8W'2+eQc!ug7`!`3:cVk[,=6D.@rOoj^jg_6F9hZ\'^R^&.YP!`1 %'TOi/_l7&.&lJD'\sV?&oB<*S;H4@.nB5V)9`=e/#?)qgI%q8;(Ujg:3_;!S'RD>DIXZ-\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&<jqXb+X'jB0NCI60i>aW9409PVo/[cc\*WRUG+mug,($%sVta.SZ&IJ$:&/j47FnGJ$? %.<kX*GTDc)95=BEnb<#;qU48Kq75;88&+u[gUi6!>hqJef[O1d*qg)q%>%'A^^P0<?c9lA:E;HHH!FYLVI#hAZ]=53q8@DF`)>DE %mCWATr.n#V%o36!hm<>K7Wt]3q1pdu?AG8Ym8d00Us3!>#fbRsP>^L(G#T%]j^oLDqa8Kkf'\ra(/Lk^1J-\Ep^sU2\'CPspL,#+ %8VPhfn,?.<lg23s=tFlq9g=Lbqs9d.>+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/;(@,?*G<chli</l/ir.)!1[kek707)4SI/0A4d>oR/%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<SGBB>& %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.%<j?"S=Ad6$,-OKSF"FZ$$*q73jL.3<p3l^tdnL>_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?BT<YN:k"gbEGp0P)A3Yu24S6a8!bDY\R?&f(j10A;7A``q$L'MU/1/._]F5_eH]?!^]@=E1MSr(G:8E4K:YG;_/_6F#d=rU %#UJBC_'+u[e>n'=eM,A5-uMp`JQE!m@u!<dm`Pm],j^c!,6+CJ9o^k="<iEqY^6SOUq?mOV_>kV'VYciKEu&j8@TPYkD:5oEoM=/ %:PM&"KWr](`g1<OG223t6F@rC]4l:NVeq<J$Q+@\ah;K%%RG1>"Z0g&Pn9&sEWHj4Ggn`:)=RZ;bLhY2<!T%NQqbJT:E-=8C'fUr %6u*t=<s_&(d>KjH*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_(2PX3Zk<Mg&;38o/F[W<Zt/B`:Jhn-GB9i)^!)`AG4fPl.)r/57[p+B]M`fl;a_<10WZgZ&N^IlZst:KLB=:*jUVj2 %fYAd@J18*1Y-%j"6YdeQO4Q#.J5Cg%@I$O\;d8E-@K^1F4^k)+k8C"p3Xc-Z@;H3FSQGk,n>N)[i%Yt'8HIDHjq5Tm:m"NhKf@%( %A-g#F-n,GJV-"Xoj51utQ.qC&(_BJY0_;n(6bEGX$),4<Je6$4J4n[n%3-W`X!3=j,@DUF5(&58O*\QnCC70)U$0P46HrAAd)ZF[ %Web\"6j)CMKQqqO@)Y35N37gVHPn'sL<-6CZ+n5T!#Tnfj'FkW7E\Q59,)",'1"UZ))roI=tG]VJ82l..jg"l_!DD4`53\ublso0 %8/.1ip^rnl5i3,<@&S^6>RLSffS1(`U<NmmLA/A(3MEG#7W/j3&qrc=OFl%&S:QIX?c(gn%#eO-",-OPCcBaX2DI<4CLCRE^D"j< %!=MIaPR^0uBnI"VE7^g!Ph$r,fWhh^[4DGqfeQaV`(_pRXVhiM'J1"5qZLmrL5J,,s+i88E)8eC$^>E: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<L8Fnb5]\6uM %&D86^d'Tj\/]BlFG++UJ9W=RYj<NYB""X5j$:u-fQrX7:!H*[+jI6@tR'<1"W=7I2+ig^@;FQfE:P=mC0$XDUJ2G$cppUMOF[HN: %Ujdl.,hrtsMDkkBWONiGs6a^dVX+7h$6W&J^V9RsGId]lIm2bS(S(!(^OQ+M4FTn-^W@`!cOZqFISN\[)nD?V-i:7G9ZeujM^@MF %OhEDhnq/S!WE3EV\>"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<NBWq3j7)7kf-R1T;Yj$^ke%c8omb@k-(Z`EhBA\ %^)>^>BB<Z,W<0=8<35Mg:Sf*^<^D2Nk8X0^?9O]D.eRPKTj>j=;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^Y<ttG4#kkZVk5>eno3nr)Rgk(LPBoioV`>pBeV5c-n$njlW.:&@MJ[_q+2-3"r=LA_"8F%j&<PRsXGL_*e'A %CS62)IFi=A5\Q4A,JV[C(#KCT>P@?W-ao%lVO<a.2JlPYI7KOBM2dE_\'iXB6auL^L^9oJIXG>>$QL5jM-1pk^2`GukTrs$0-4=Z %eURI]ge2u1pCKJ8<MjJc/q$3*3dU!',&cqa/VY^coI40$9##]sp\O#c$>VhSF>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:&<H!Ie&jdl %h]9#4oEiFKhcR+#62OW]+5D9\*1!ZEbrVE(hEd7u[M4mLo<X),/8gsr#K,d[EbLeU8<[EPPo1K-?72(NiGC`d?+tJkrVUTmIFs0N %[5p._`[H1HSCr`pHA\YRG5ui0"$=;`eW=^g?A%"\7+gJi4/.e]lrZB_?iN\ZF>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,<jZQNT#$6duHg`DEbBp+_R;B(O %\<h5@gc`&W1']3tI_fa-Y+49.3J6"P:MAJT1Z.TQ[XRK14J[V0:YL-[Np+dCQ]>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%*u2<Er*OGupdi:7e\Ik*!5]/D%;R[B6Z?pNF,>6\]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\B<s-4[5b]8998$%A?N@WC>Y,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!<u=J(8:`L*n>!^HGgU_:s[`XW!\/g;mSkmYoP"@#jk(=3^ch?SQ;$%Pu."1D<c %ILp!h5[.K4i8r`1cb`XlKeP%[:[@[u07ZGmA=b5TJ4Z(1-%J2*:gBkE5qRbBRQ^KK8K_apG];&E9>D1u;^:^4Qat=FNNH&#Vudn2 %L-]-]9.$+-@8+["!@Oi\:Nf)p[q_HA+BScIoFTatcTkQ5LM<!/D@PTFpP9;?rj;aWA]WEF<K*QAA^bD]d@(2SD[76c5WD#`EYqW' %b`Y40MB)ob.]F3b7h=!Y]$,F(*-6.XJf5hq'L8H7i^3;ac`VmK#')>#^9,Xam%5W@EGa4Bk<SgSLYrD\:CF%96tqFpfI>K]TeQj* %$Ee9!k;^kCn6I`-)t^f]9*N8Q`'=a<K,.D(qS[.X!#J,:^o0R5!;L.4^&\c\":)K#O%pf6OJ'^H.En,#QC--G=iO6&kXB><&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.,(C<Q-J;/d#5QKda-7C1!GQu>U %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<i%:?QMb7Sl1Sp^O0WnC:k09:nmn1Gk-opPkBpP)`T]]NVi\5qR>@_o'.%d]H;A6$6@]s_>^cm<?"tN<5NRAK';r) %@m`D@TPag-`T'Ae$1LiN%e]VA0/ouM49H,9:\7o^Q[rW+08VT5,eDq`!B`&>]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 %0S<!h:Zgeg59$2mgo_ME$s2]A<O@4-8^"kN1[hGLd%c1OAo@GDn63Zn;nDMbofI848TMb?[p7,$4hhW.C+=CC-OuZ;lqrB<T1[og %X1qch)]PL(HXC.*]tR;;R]CNLo_-X<os(u-Qj3\df/J6hGEo>ailo'XcrjjH@!f:9++H6Yqa.9%42HPhrY$D7W\\VLR"<M]GBV[? %KESc.!UQLdT,c8S?D"H4>!r'0lofP2;'7\B%]8^OX.Lm@k.h#RS'`oA4WRg@qqm8p:UabNM@\3E`q"?$CYN\')"OW7,MniXLDKXJ %d]IAgniYX3LiV_3fMnmDe4E3ddr<Uq+5j1=]JH"d*',i,cAcc)okgSB(>>o]<e]jdVMnfhINc:m?_>#O9eUn1U^?)am'<1q'F/4P %>if>mR<%MqOdmUS:)uE;C^nMrQ^mj$h?AMuVote<XPpaKEtF5\J!Aut_^<X1Ec!o.SIF.nk.jLCX/6dfh"fJ/r0Y/D7E7@cU45?Y %ECarulb:1;UoQWE_!P?=[On,Xp?:VE?_X4Y-qXGXn=Ad80:(U+eRXC%#sGagpctj.#VP>q=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\QH<SN(]eaOaWs:%+H6[pb65!_=kg%[se9nVhoWc$t>sYZ/;^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?<L;&2LAb,-6^RmjN>clq4:gTY6GR2o,-&ojirAIa:9@=@3o %(ZCD<RgqQ[&=FuOOL+FMLEm2]J.IB?cQtj_)&!7;U+T%6#/7:+_%0ZqJch"k]U?KgKgNYDBNlfBV;nUk6po'S81HoMgG"c>[@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<<rj;'JF<#"&IO?j<&#.+A-YI/UA%;-h4J^;TOS98Y=ANoS2Ul6uVe/9S\HEEaets/&PTfPTVa:#_sOCr]-= %TCooW1N.dE8;`MRd>6l=GMg+.gjAJ"kFNF_LG&d!+t&JN<n&cs<(7*4Z6k,Wco"^5ab^l1S2Nu1SaXZ!Z;Jl4O>TV!JM,f133=J= %Bu2-l+1d!C&mg7>l=ptEVp&c9EJ5<r0<q;d+b^VGdd/C5ZjE\oeL"ah^<SG>WY0M8oQbIOC;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<q9j>+sYWZF%YYtjO)IBG,jdB8ZA0l=DJS<l)lfU@lG^]eg=WtR0#-4i^,XIO^P_;&pAq;[udG?D%IJH %_Dn\$q+s48"sdVo&E[tmN%p"G.Wgh:PafHn3e>JbYTqGZL6K_^n7[r#';NDgNDtG-4kS,8Rt+K]YRtFm,8-#g"!e"mBHl<kjsCmK %J;0%]'TbRn(?2SN$\Cm1lZ7Oig#8s!3"F&\q^KlG/9jSX0r(fSaGE%P$-(=[Ic.(Mc?I,rZ,P<<],i#_Lf^U&a2I;0A+TVS]<mTM %Bmj_t,Jb[UIb`JBC([u\,,PAF-$'`/5m_26G2fifH,;".!\duM]ab.r\1Y&iJKs$g3X?/bKk:aECDd:4)F&phM%QBu*Wd_=,.<.1 %^pG",3#6rE$6>=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;*<QL#+o<j%ApF:% %&eT:V+EUZO)a2H*N+CX?P*MiY$Rcg\V[9Re$ZVj8+1jL/)gUN)eg!P?AFVY1)PX=oNFiPe3]J-:H2tONqkg>1[_CgE?029&UJd-K %f0Cp,b])F^m8<3Nd&^2!4bTE8>*)p(3IlinBS</pkN`kKc8l8=LqD9%qijK(a@t]m9k5Mub=iM7O\Uq+#h$8uOsZ@9mprONYm-&* %;XQSn7f`oROr.B#fd7N5CPBVn$E'1%0N":S#GEAD9/4cN64QtMKdL)t"Fa5I2JGK5<)RHfPDmrQ7-Tq-g'qV,:"]ObO;>A01U7L& %A16Lc0J0d\[s,t(cU7:`a<I5RGZA/\E]En5>(PTalSYX&>BIq`VJOBFR)DqAeE\IWUrF[$3<#O.JbW!63?qU":br/WShmdnm#(5* %XsUsnfo@u]9Y<tG"Ra(c67ZJLcmB\9-;@;f7Q19i_E`%<Z/\Da`6$Yo;R9@G2\C!M<h^gJHq$+k*ThbohPiQu#k-SiQ:NFG<$>TB %-#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?8IelSRgW<?)`hR8fTQO-He%`=_MSs>fTXdKI$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=%LE<C:Z<6(i48S00DXi$_,q*bgE\SRO!h %SOfOr9p'ITLoVu-:1aX#XCsaJBL@mm9cR?SM`;+C8UNSo0N=EO/,(phY&A:c;?6pF3o*'LY\VHCBYq/$afTG+PSqTrFVGGl'Jm9m %clOqnFX:n<ORJ"2h7I-GBr=?uIZj1<n#NUJEYN;"9jJ=;QB'VN^u`q5#p;KFAHQ]j:P=bg-2S`&/+\!QF_*jROog8M*p/sdK,.=P %cC*B),L,7RU='1f!O5IQf3rHtedPI3\rV)Q9?=+uJpAP>Fst`Y'fE>a@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?/Hd<aSS*[S^OScknRce-&Rq?^W,6j77\+Ar.&kBU0sD<@b95_K8+,%;Maa##qP-t$EE'gnOhk5TkD@0-.Wbd %>T(E*`,F`+W"er1U0q2[ahWd"A0Q2$:3mIFiK_Q/8K)<lXDLjL2bZY+AudF4&aWV?nWA@OJ^Fm7Tk=a#DXL7:`RC)!aNI4pXmj+O %[IuF+Yt9e29o*<\n#r/m32Pr7e?tl]OmLFrs"]as:a$!HY.G(Z]u%S;V&j4]=5.djpbIg9lB?KOQP%\;9H3)emkl["?7_dI,s=Bf %S[m1#lQr-@/344hQ$oY9EC@[[a^2o:7=b`dl&(n>)X4)=<G33nkNEc=+C=7V7pUulnI%]=(N8Poe)#-ZJT2a:/2%$eSD/\sk[>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\<JV&l+He+qZ&EuH*P;HjO[sS[>SscY!2V-*4Y;V@;*LgVW7XKXLmlCQ.!7E:3*hAc?\PY/ %c(LLT&ZssnQ[N_-f+cOd<uhlM&E?A^_'VEF;#6l_@Ri@^4&M_^'M/d9MCajf6@@goM67V?0#i,gBY;o\gt]r[II'!1)6"L"XKd`/ %M\<,u`VPe8^^_!:Q)-d#Z#)L)"6%AS:!N9/jlseM0+sp^@':H%S:LrSinXW':j=So8P]6:')rg#[F1tZmqABJQmi2n]AtB3(q`mi %NT6.<"VO3Z:=2pd^hQDf^lfD[gj>@^fL3a&W*qhejMNnt*N6Wj.(j&%RYWP-Qf40M'MfCAUlEDqK?$s-1a*m'H<qUr,kF$rpH99q %@q!=GL$^puR;.N:TbUa?(LSE,/%'eFcH1m2X73[4Z`o0hV/VrjV'FQ*C7:"Oc8?tjN#,I98YQ_t=H4"u?^-Mb_Lt2t)CCVJGDSg" %8HCE$SQ&Pa.#6lG=BhL";dou%g83hn$RJ;kN+"^KZRsPDK"\*2#,e6$X*oa53RhDMr:*#UI[!/_d06;P_Im/0Dh>Z3+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$HC0O7Lp<q3T4+&;kuS)-7=dQK6tj6j=LO4]Xb![M)5l@aUPV6_[1s_VM!khf>R%#(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<Z8u;Ajp(G-@;'LVLR%0k %Zb@5(2TS(>&#*rX2Z<A\^<sa"V.CdNRu?^`Tbq12N<.!5E?3ge6u>'JJn!=u?I*0q7(3:IFCGrt6pg>dn\'o&`8*t?-I<Yt*7epe %9VEJ%"Tb^-W>T^#?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 %<E48HEYOYd;h&u6r>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^fq<X<! %CgNQ:$"f;6q\kb(-'QPL-Kus5]9E;G_AQ(X'`2JH,+]X[$h-M@aJHJL`D10:#"UOUhl\@t=](-oF*s_f9a['g:`1X.95Khb2Pi_R %P6pdI-_4HYW"C4ooSNfiBT)CmUaesKPS6D!>ADm/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*<G;WL=de_&VHgg;'Q2j78n$!#W&&I %IdF@sMsr56(lYhsBS@f!7G0I;_ehAaU1[m#k$6$TKOo&^]53r^d<2ZiQe#Y]`n2.HPjqoNc@rpsYV')VMN1eR\m3;Y!#EBCD&JZl %>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="tNA<N`$*]Sg@pnobjCS!Ch*bH%AiuFB"Ai2<t7VILaiPoo(h:h.2!j %j0).l=@9@4MA8M^F)t.09Z/q]D@V,A04LZd.&a>mb\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/^)+<hFjWjt,^&0I5d>t&,?>>:+:cZH&4Rg7 %r`K&NAaD]Ja_\qL%3#aY3uOn<J/]k@jMSPLJIo'M-7OQnNhq@?SrDfa25K#Xn&/)'=RNH./^-!gfQ&,hrSg:814C8F&d.e`ip^V@ %>>F+5jNWJn&dY:ph[ed]X/h.R1q)KFdM'D&NF9n<B!,5)Sup4N&VXCMBGK`fi6KOSAIArFKmJQc[N@HXF]&"r0Nc%+!N$2mS]M<d %`=Z=nf<lN$)K72a!\IAkMO6r1l_8$LMItCl(N5*\jX'pW3CE9;&rEnf-$K&E,695+'.gL2n^K:]6#d>S[PburK2a_;$G6[jNAqWW %7_`$u^"i<X5O'.,La#pQ6d5psbJK`)p0!Jc8=)d)@dIH"CHIZI!YLJ486P/3_HN$]WM4p17A??dR-Zis<+X6a=qoDp@:^OYDqISd %KT_u\4eX,^"*RS0O<WY\HOY[<eO%&\A_Af@3^m]a:7r&p5V+(mH7;^MG9'W6;K#SePRW7c+*5h;6'mCk.:jbLJ\W"hfUce\-7j1' %R7YSciY;(:cB8C#amk?M0W/qccBO<IR0V=e*T8>M\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_<r$2+p!SYtrOXiLr1&ZXY:Oh=MY]i\QO8CPmA<t%hQ`I-m<5o;s?V!o?UPppf#AD_%#:+RZFJI4Qtbb]*] %aX-6rI.l024iVO9lk"o%6jTrcO9D%2JPbU=:>%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_[;.<N]cVWW_H#HDI`r=hW8Wce@)l3QF?+#N)%M\DN))7be>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%]<LjNcK^Bb %Na=;<F+'>%B`<%Ig3<TTG0"3e.9_PT9O0Wc=`&pV1Fgrr%tgD+5]M`LfkEgI9JN]hks?pF`["6%bU,$XD(K#@U,'#2KK:UZ1ebC! %%Z;1$>9cAM5=<Ft?^8ZB_rUYkl6<:FO=eZ,A&C/I\Q)BGE5ZP#$4*b!Hj[to;hH7J+YcU1b-Jhik&q6V=j]uBZ?r959h%qF,eu)X %5'^A+\"gqJ7NWKQYG^St/3=gH:c;?1KnYQ`\sA2TiKCE$84I`%`9]5+cFS"hfs37C&BHY_Kj/Q`"jE]dm#kZ[Qk^uT)7X!rb_fON %MUd_<P/%1)AgX[M6n?YQR5H*I]pKV*#4NZLfd>3mXH[&/D0/>l)*W^i2S.t</i*iJi>ar$#]*_)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$<REebIa!3Xh*%oE7!^b-<$RU"_CT_<PPGFOd&N"/cqt1K&Vtgs\7).s %o702_UMe9^lMJF`iD(5Go0;!uU)T8*"mYt,M_</Y`!8=4QA_G;d"!#nRH?GBSlh.<!i,#"6n_iAQNW!^`UGLGTY3Kkd"Y7/Z]!'" %6GEI]5"WT17#1,U)W=&Y0OVJ:%bY6p=n5Q!3=_DLJ3sXAnU%54EBsMB4G<f=*E.cG=@3BKrF:>^_"3Q+5N\j;BXuM9QI4P6K1IGt %d,^1Q$_9_#6fT1`6T(R^4[j<T2_UD!C4u>;(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<om85*ER82BeG<',0Y%(lC_Qagjk?l>['Wj1r#V4a.?r7rEuiA1,6Z]Q5_Q45m_7p %"$7T>Fp:(uq=4s<R<W,49<UeIPA;K7c[8/Yf3tjGF#nOSJ[$LgMe0=K`_08Z<mIDYeaRsGg!_6BN[,:R'9k0!G\Xf*7I`XW31%0* %XKuRqc6Ip<:E(;EC.DASC^r$E7LWZs7-8a!"ptF30!NuZ;MU@0SW856qha&UnJ3a_6&tM@&JJ8GYM52mD]Z4hY(W8&69F,i88M!T %?&BXaN&ZBj)3$=ZQ;</+*-RCG0=:N"I^QmB?8R;]WUJ%?c/(j4CmR%2HDk"^H;b&N-q<kH97b<J&G\.a7J#"Ef\QqJZu&uF,t;B2 %,_MI.\YP"HJ8DPpd8`3t6]V@jSu[/@ghoCp$X,q2Y6FmkCcX#m/],o2><Z>q?/DcjKeQDr:K>)icS++W&^t::2oHMRYh2gj!%;1` %=k)]1.=Vt7Zo.A%N&</U)`'+#`ilIh@c[I)PK72n3I5,SHs^i\Ad.h)Mq'Tn5;^Flk]Z,Kb[lilC7+fojKBO8XC+jMS+KF>ZGZ8Z %7@LFU752XihSm(I86"'0=W5Qh%D2D.6n_<P6oA#.!(>l\<rY5'N'b3>_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,hZO<J?:XJFLji"IG(-1ur=7XPc`.M.*>aA'&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*<n@:4(c%Qm]g %JkBE0,]6GsiM\0b(MaR^g<rCo]rkVi6bX-'WT;1T#3>$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=<sQB\1iZ23]t9*)*3OJck7BE*HW.YC,ci76fVV7oo@j1nQ'5> %U?#KP#7>'lP?U0b&kP>e,DdZ!]Xj%V*em3B(df.'6Qlm7X^FP-r6#k7@BBit4q_.HCsXpag]Y7HU7teQ";`ku`#,&J>?=85'1iIJ %f7[Xj4Un<D"AMhA;P58Q1(<j>W[e`(.ZQu@MDb<aYG&048Sl'f,\+W(FX"G3?m]h:l2YZ-Ztdfj;%`1L>sV6fVEcQr%;[2J'"sRi %la[q2;Za@ME@<EQB<W8(>3+Em9,_[?ZmV,`'EpV,'R!YSICt(ts+S=fgZ0n81.CcMbZpPTa/5OZY9-B,:ia!UF$^:B'T5XiPP1-f %$HZ@c,jM)-]-oNI34*D<P&;nZ]L_BBPYJJ\/KJL9e53>!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<KY$"m"R1(J3E6\&$k"GI=BTg(7Bei.U<.H2jN*[80@CL8'm#A(hk^nDGr %Z8"dr_o'REJ/V/Rg*U<idVgHdW(DdnoS6[?/_fE9DeKI*,tj$pL"kQ\LpIafY\jbP6)irpH6_h$5iQKeQrOeh1bcC68+*YsQ<=8o %>%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^<FEe3#SkGG0_A&OF0Y"(P][fj6r$.:r>h:i]3[%sM.Ef1<Q(hPiC52sU\"t^!`tcs>GE9%Ni-LAT(0Zc9oL<j %EWZcWWj[bK$:K=8RjRh5MT.b9>/F!mJQ'QVZ63%C$@:#R@Pk)m/AeuC#M2E9fddDp9<PJGi.*)0adU@9/Rapc'[EZDcsB_jBLmq) %aZWt7*Cna,8ahZu+XY(iTl/;bUNAG:1pqtsTT5!:1nk?6'm/!uVX<\LL<^`2o8Z@jFUCL\q(RnM0Hq08c-R8r6FG]@F:K=N=fd57 %VQ%OGRl8_o=-e,)P")G((adF@-Hj2ZK61Xmj('(rA/P,UF=^cCf_I#*+uQjMJJFq*_b[6$Y0[4rQ%/uhOR,f]!Ja.FmY`e<^*d&T %4I4h87A*'-)T0VIE4IACEg!Xo^'NMB8Q:AS.#Pi5gqSS7O].1?KrcPB3?=r<ZU\AJa&*:*E?W70P&):U_KCYkTLF>UqON_-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%<lbp<4^Jd!82?c=QinUiY:ltAL'-AGHF%uC$&m^lc,c2s1s&7tJP9cMVVEIb7`.qkUh+"=1r=G^WFAE.+<kbT&:HNP&UZN2 %JLh,Y:m9jql4;^P@QDp88<p7c]VU@#,mCno>(mig5lBtYKHUm@#[dX4H"s0bkTdK4Xd/%$B\$/JZtJV\RrtBeqBK3SR\<l$d_Q]X %k<cMp[TU3mK?MgLB5*@31sCsK-0j\&X`]AI>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'*81<R$N %b&&7-5(^.PSut/./>3@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[:f<HgH&59G@X/o&VqBHAh\KuQW\=b.8C=e8]85]:,`>s7oHVIC#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><!mZeibtCh&l27&*4-eF<V?5?OY*'HPnEPdhts#cAUn>.](O60-F\6&8Eh(FLu'T(E2nB"$S6(pe5a^/@coE %>.>k9;L9&o$KKp%np7`(PWP<U/_)lHg#QfjddF(TmjOlXhe9"!Sc;9@96PY!"MB2Vj(Sgg_fe).4H&oqT=^2;?@3I-4ukAu/M-%Q %r+D78;$\8l)]noiFf\qG4n$QijL=;g+RN%HPDAT(a4m6@T:>k+9BpT!a)YIX\'4bkX,!5DR0=^S'?hC*a=Vu57ZHnto_4BCn'-XX %`WMABs1IE>a8C4*hg+hSdBo"L_%9<C8.("Rn=>'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<Gjhql %.m"NK>/'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<V+<5Fi5O@6V`4`qC]Dni0^p2D`W/'JiCkT(&CDBMH/FJNmpMTH<0@(8Yi?&%TbTU@"i$p9%Cj^l3l##'"J.$&X %!'44AfP@_=#dgFh'/ja*<J0^IbJ7r_6F[/s!sm1e(c.*j`]k"sE'/C7Gkg3RnA1;#IL#,\s+YQmdn]t\]=`a-#Z2J!iFgKf<D_O> %JA[gnb:NreKu!a`"1m;8J.9+$?@`oDMc`a$_0!M""41GB!GGblORD.Q"#b&SGRm.H#hEW3B`/(cn_aG&j])0)C%uRqkY17L5G<Id %!/(M=m+`-?p3,<uWt.1o$UMCk6,R;CdKkU/bak=!\nb@If`>gij:l8/XMRX3`9!JDb6Og[0GE"ITS#hr<O:9_oaBBp7Y;lS8738g %<*6X2K'j>j[H!se4!&h`^,tims$!dL@434V+$)^2#H3R-FWmaC[0$CH3`o<GDa\F9I\$ZjLKT=:&).5q+WS!_B4_4<_,#:f?AEt' %aNC>E9X[.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!<NF8$?dM-4ZJ(ER-`O[bJ\Lo8NMg4N[AR7TrTg-M,8@>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?!<El=)M+bn`o(5+E"Md)nA9/dYSpSoq6$)=m0UWd-ETrin@WgCdp&ls %>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%!!<B9M'6b(Qtg0+n-U<4SN>,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<<mFO-*bm4gEGSqD"/pNF_cmpPGKQKCN_g`m?$fe^;>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^aC<cn]V[F<$[M %8&oVd(nXglbi%7RiZ]"oBDB=\+JEDU*#[AQ_XPA/"#e/&F#R?$fA(l="%5RjMP>27`!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=PO<Ht!52r]atR>X<%&oU>)-]I$3<J$jE#&VB+ %l*8_Dn9#=O-c2Z6N%g#mO"T=U6ff?S0lPp8<J7:ZI2%JbW^.^O*U"tT^,m,bIPr^^S"Z<t&RhD7gk@GfAe6BCj.s@WV'kLjq:g2T %^8=V<+*rLPTL\5?Od/RJ%&CjK0SL:S/HBo*(G3Ei+'Q9\[=i'&Yse9R2uO,t4G7k`9;(2>g9<qX+$eCd1Q8EPL1Xk$Zdf(\:uJfm %iQ@Y]KpK7W1n2$k+nQ/U:MWPM-@cOZZ4Xr_['*$RNP&+qmVXIV4Y<8N(8qsu;o=u)a;Bi@PYp+.s3:k*k)%L>Ho9?A+`s+P^*R?G %<bDtg/&(WES$G.m@?3>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)T<gP3&_dgZ7eXGBi$[9gu$ZS\3P/`%V/5YC?Jcr;(pAbqK'Ws7+urrNi^+k][L/F[*NdEAI!,bZf2@WlMPc5C!%XO`HGYih+!O %?iHPhA2Ni2^\d-mp%s7.7t:*Es5X^*oGaFunK/T.@R"mW\u>j+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-<WeA@ROkO2t>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<iuKiUp8cTh?`?iAAjrqi&]Btef_"3V+56;oBUFcVNqo-aRun,2G-I,jZi$X$1L:&XaWI.<OeoR4]mY(mW0 %Q);_(iho!7V*G"62lCI8Jaf.Yo=oePZQK!U,DWD1/oPE<T_trgr7[M;r+gf><_Lfhhu;<trl:)5Gbs:hi@$VWQo@jXVS;p"G+8@* %YPn9`554Ss;*O,+]#'Psff&NR0E9`RIVAYhT1dOZQ;)f+X8hcUII^`G#K%7ecaKgfpq8"rT3YB`kW[au1=1GM9kC*sCn&R4pXqE8 %d/D>""Pm_FeDL8Ymh!(.8Fe=$6?N7XNrqsKU&42G!d&onA\`ZZqO@?HQTtN!miF]Ug,ZKVMK[:[WhjpbXIEt]3O<NOCZipQWTK`r %<0WkeQ\n+.Y]KeJ_p0t>R;':)&^u/9kPXidJ*G"gG<!!Ob_br]?i=7iEU_aukOZs%s4&3@nWuK<".'O9H['hI.Hfo[(I.cLhtj+_ %p_u2Rg\H6aDu][7?hqrRr9t5U*.ft:Fq1+hO'%0-^AjJEr5P*&kYM6VX6k?7r(m@^GJEY9?M`MO#IgOu>."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)<j/oSpHP&f\M@3g[.c#1>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_<L_HJE!1;Y/V/U6BB4rEma1[&e>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\A<R@tje)N_LA@WEI%`?Xb&5o,ub=N7Jb5ILMj<X$&:J!UG<eRFQ.%2:i %5);qu5Ud"6fQ8^L]Ak#5C+hR\.8,4,Vn*8"G_gE%qVC(#AUJm4I(:j2*cjFm?=o'+GT>Mmi[gg$-sV`\_&:ea.!PgAM<UMj/fa#9 %iuC[b-:Sdm,ZWH?q4HnP;h^>ZEA8baCtXt$a#(l2ln,iPF&T%%Vg+l6\d:p,<pLn^FR1"LY-7G=\XX`l%d,HGo>@?Bo#)kIHl-?] %_(M3*LP70R>nc`=s4ht3UW^Xs=EmqXUf-IX0f91M_6Q%%J-UEYnq<r$JA8WBa7qe.=UX"-;3:Gp-<-WLNr`j8^rh!)<54p^O#jZB %ijS^<4,0pBR$"&b`=,M0Y@7,+U,*C830*722MUHpn8+&%)0:&Q]puKHmp9VFq8c:]H`Xpap85k/6q0/5,c?*UKksV<CdcN"8d(F/ %762tmS$\V6'P'j/O>ctKWnfLfYu*<=btjEQ)/-Ua!@cQ64li`-XUD#]`QV"]YgaMa'GGQ'1,WGY`gF,PoeC$EK`T[*b[l<pTn@oY %,p]]?=`hMuf+'r32p<bp_3-C//tD_]-D;_P*+1U:N,K47$0Q#]JQoaj"\PiH:60ocGfE\3=[Z4"P:AH)Lhpe`8_@kEYe?0F:,chb %_7PLDGU\THqbpuYcl#@O[-5LDj=]NOB&R>jqb1a4&_H#,Vi10RL_G5A"SurK\)437Ds@=7,e80;3gt3[fAB5j^#-J_VX=>*f5G37 %[sPmGq^q(/QM:B5%H)Eu[sNVb#B7<L>LNB_?/P)KgU?ipq#T94hIf"0gV(qdhV2c3L5^D$".7o3g[BHGen-l'R<G.Sg?cgA])g7; %K9e7ZfVdLZJ+<)Vmp2MCq9NqprfWQ4G9&m25HlQB<O'JFZ@(c6#<2&iH05G3<QSQj7ro-BZ3i)K4FHL;l!M0ufjB!'U,>l/8<osj %#0rIinXfLRfZ/L+=$HHJ9=l8+hWHIc)EB]pr606NGgC`opX7*2p<t,^Yn)6GA+(l@D7FdE5rA&5gl=bBNZC?*jW3f8s%BQ0qOrta %p5pGDku45Sge5tc3IU.aN9u(5.GIPPYrF];]iaB+TD7JCI-=M$s88dDllFEC3WIp-r`%Epq!l8ZH#;.rC500Ea7m=AM-#2ho:LO" %?)>F4I/;=LI<PA)LGk`Rq1E<$>4UU.J,M_>moA,OQ%sk]POBoLhkr^;?&(@W3WJZnkLZn>Z>fKPHf$o(hjk^XHhE.)R@:1:IINI# %UqcEAb>5<EG5lQ\Y^69h;#$eMHL/?\]AE)34*E3j6ea`oA&%O$;\q[f_6XY*Tr(jDR(%VFn\FE7n+k%G)jdL3g@AP<r5Da@N.'cb %X57&>@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%+7<EsY.<bIi:*`;cfUjJqUE;8rZ".Hou"Q;f73c!]rU>Qe2Z>@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;-RN6f<Ed,c$($n&1GitN,oAA!c!J5OAo#:4c$@lqGc61m&+l6$l=43CSiQ4RL_?N(gYogn0I5**[Fu`nj>M5]& %NAZksgb1-OgU8D(\EE[(<esl^".'Y>@g1A%pNI%GU5R51WD-&.Ce6\?#kQl4km2[ZnuKb)Eor!/l-'VpM7JTPCUJW?.)o6Ln+_nk %4Yc1]%us<oBO"kflu5^@bkg]21'Ohc=#<i@rBq5k!nh3/7&X6i_ahbAbe4JY1&7rT3^g8&@5pr(@nO#m`EF?u6*$1GgP^/F&3PlX %fNO7,>FUf0]YLf.D:t/A(DAa]3a+#1CoF%GEl5mN[8A3+:LfH,<Pgj$/o.Pn0Q:12N2__F2L(Hq2U8*D[+M8.#>u.:U/M#r^5g0s %*B]SYP2)pO;-)Q#mLGu4fD[L5XqV)S1]V$F.3gAYH\!#C=Uq@"`)[%lVAVX)Co.5--Yg1D4*9IV\SZs4e^<sL>j'tW4b^c6fZD9( %*p]\^@UrJL(mMcp8/=jU@<SA1o5$Gtg!aCs9NuNF#CoO%Am.a`Kp[]ZZlA;Mh*0N7f!Uq\ap8=pou1Ug`MQ)'<b3oL8!'>#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)<prI,7C7_G)9&"B(DL<XP&gq/C'NI*R\iMmOIXq+g9)4>! %<;!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,@[ %<st9o_pGum@/O)Ha/Q%I\M-'=os8qZcU+I-WTJa!<j#tlR@6a_dl09lgpSZ`bnp[6>).#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<u<n&jokCR&H#]<_50]0s2 %OL9RHD"SKt#.lIbAq]ldSGn;RYq7>>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%:<LGgL/6%/1*/95SY_FN` %q,4B%EiCsJm'*Ak@jRZ"XW<Gkgs;65`pre`*4;0gd/dau=!oag@18S;HADK*)Q69aBu[]CX3&fP8A9^'`Za97CV^V0*Q8V:"BZiH %'t^/t,S'd"TJ6)."[Gf-(HufdM$!t5Hu^&.<P4RNOmC)2V?YCV(=Elek!]RU2F?1-KP6a7hF`'<Ece#j-;4Sgeo]dTFMle(qLpBM %"(+i)@fTOSG%)!FS/9/ao>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<RIm)p1l.\n[`Bl![Rj2Gc,fJ!GSe3@+hB!c_nMUp'`[T4td.MW=):M(?5Rh6Da3CCGt %50?gBXr.IB&)01Q'X5FDHi[_#+LnQT\#4VA&K=*'BMG3?B1IaI^7&5trPOt["':E2;P#M2(`CE-_P=u9Y/rK3iE>\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?<Kq+!>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<n_#@+K@F99[n"FD7i((>: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(,<bq:*L*3/q1%ZBdkSno.lg+Z]gIk/,%F"X[m2c+Rc@$74ug2fh0QY?n1i'$#R1VMAKV<=?:uOM"SK %bFq7BcJ13o*4J][X(&VBWNG8bqJ>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/Fb<WpO):6LlW;0l!1b2 %h]dOh5sr'N7qW=+=a2`<p9;s"I2U3a$h'Ng"uA"X@d;W`fl9:S01K;PfU+jTn$e&.GuI*!qJIK`2@N_.GqPrc+<R4(bIpNaT>ce; %%Y;q3ms;F+e&`a%/b3TF,;T-bTmSB[]f.hD]_\5$^&ajsIJglIn3aR:D?^"j9IAmAp]6qsh%o6]&:?\blUiSGVs)sd$N/i:)uT2" %%6bI);"d*?!<Sm,SeG9UOpnQm/+B;m2>Fi\&_"7#p%]T7K`p=n]G:53Ri6Z8kS;bad`6gU(AejDNZGLhHE<S;s4gF?cVjLdZh2BB %PPF`7BbhW5oW)gZ4R:Lbk0>qBFf'.Y-\<O`H@<g/Wn@7Cd8E<u_?apIDZ-Eu9dN)K\tc3@+gULB-U^]g3b(V?/X%SWDo",SJWeJ1 %:ib]M4@sG[[d>7L-\35TCHmg]1+jaTBC<jo&!b>E-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@<jKp %;`@H.EdY0?Kd6eJ99oEC'5AP[/6maUV(-$6QW2[HWjIIl@0fqFbCmnU^3Ibe)pKD6Fj)aaR"0l"79FTfmG_-\FV)c"3B+]%D</q: %<+dFW=)(Fim-D$@94S5E7WR>!+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>\<t=@c %QR43DCdD<)Q*q18kW/daR&JB$K#db4Ngej@,Q+*#O^;83hb!dS)t8&#H2lM+M6Ym\bk5#?eKuteeD"2$=l&]C/0M%I7:EBi5Ers! %cafE(cAeakF.6(OCtH.P.HU?63i:DD@W@l>]^`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.rd<G1u3"+F5[Rs %\MnXJKK1K:S7=`?F%KQ-*S;WA>45b#[,-^EMsH"5.s/\[id.u$D^hr0HIR\b^,s^tA]X3WT=l\*SD6.Ko[?X*T.E]A8=dMJULW=1 %F8iVp#.,<o4u:Uf?OMn3YqhF[)PoOP?QSViac<:DR!]n))"n)ljnOoT;WlLViFr`Z%RPXG=6@V.7oS[B68N/CB1GE=MQ(5;.l*K7 %i$%k5SC\BeK`5N&Djs,?qA]gX,"GLVZ*ubT<^UX`I[+D(`LJ\67[H^,5T,aJM7e#=;QUW?q/Yoa,dQ_-ZGg^iP%E&V7honr@NNCP %BBZ<dc/^Oq(<C7nm(lI0;W'4N8=AMi*9m2k4t#5[ALr&23,DnI#@NnWPKP+T'l@B8na-?"*g_f]MYbk"ilf,^NM]uri-sq^jS&_G %AFH+=f4&rdOPHm>(rbGMk`%]AY]Ibr3OA5Q:9!BPDMDOsqa\\KPcgfojn\Md_j;`.?umLZT$dgO'Dnf)R!p5t!#=ohS7Dp<W%"2u %VV+BT-\2M.\i*lqj:2/%<MYF":iT+UL(ss61#sPJa/.bkb2e4kclfm'#8A2agVmXeBqu(Q5F/M'^WsYF3(n5+,A7&<"Jm)S$(En< %>#lJ'EI=,K-*&NM;4hN.OMNI!0UTJd'!ko]Oss*u(XuSm_O0<*4iPT5=II!7^2@!$\%FnmWc38*K,c'k81VS,dJ<HLCqjr%'A^4Z %73L9kTulT]o['M^):]%kbNK>/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<?(UEuB%HVR;QMbW'9mCjq.D[WUmUXV=5A%9Yqk#K,,_S,lP[`Z(qH('Z.D`DWl5>%t?+XSbE.2B"*8 %<_gN:3J=bWb1B@iV"\?/2@5lcOn5Qa:=h>k*uu@Cc<nc2rgr9FF@Gfp5/;MQ9FbO!=$-bdCn\*h\"[O.hN>h>a:Z0iR_c"k2mSl9 %<dXpE07R])p6'/nNLVG(c3lLtZB\D&XCdJoMo0huDKG]Y:"N9k<G8J?Vcq:Hmr3PNh.urGe#>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#^nEhJFre<C%Q_W>L&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,<i]Zc/WndJRZLrWsSBpc/*\$31'HHMFV? %6.UKlV(d`6[kBt.moY'ZS,U]MVdCmh'`NRPB`Bed^#>$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[<m(BUHTJ/2U#<g^g[6=!Z%r65$3.t^U46SXs/-+<'?'!Bd9K-$?ULcDl0&(GcWmX^#,6K906!;*I %n<M[!B9'g*UVE4!0EYK4QT%R,P>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? %\<H%Z%6c(`^_/M.l8`D03?u<>/oktpf:O^'T$1Ook)NN_-c[0&QX2XX730h,@d[4cD%jJY_kRn@U!_<PK[AkspKcZu&EiW7$'IpB %$=jcekGl\P9j@3EMh]ud4(L%_G<@6f?.DLN0Q#(a!2ck[e9$!q=0"hsHqkGjj*=bY_DUOpF?$2gEG:h_g>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>2d<AbJ]/so>BU`h$p.(QUYa"7:R$,MPf(Z"HMZ#PP[nsTj5q)7)dBA$m\LXe[BLbb_W<!Xm9^6 %D<c./q,4q3BO9u8=(&m!**I.\[T`9/bc4ejCYILlPmaG9EPP*Zg:Rm'_e#4CC#0]Y9jbG7XJ4t'KY66>OBE>(([o:Q*_A]$^Nd.g %P,8mt^!^ucGeOM8LW1KiP=)R9I2-Gd,i[^DijF(f<EFPIfMHS_GdVR+ZrER,k1PmE_FS02"(,?aj88]"3#d0>;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#<l;`U35S*/GV9ZWkW[%>u@^S1<HR69hgO%(80R)la2J&G6c/57%\Tg,qXu93Z)BqDJHCgdLi!K5'C?/9< %U:UWjEsf^eb&D(-HDt@EFr$B`[B[lY$ouD!m`T*A]#XBl/%*Nr9AOuHBY/RJ\Oj2+H_N:PG23ed@E\k]W!r<l38?LY2QEZEh*u6; %l8IqYNOjE&YqGR*6i$"HaGokm.HB`.Wf@0s5?bd#RBG^f$Bja*"GkRlG5RX+%i6lBA!2ucA.rIQr*3fH@W\?o,U[@K+<<mqZqiL% %RTf8X;5`j[aJr7=%X1^-Q4/hD[=b\YX\^EA[2HO#Gc@>U7Q+E0CACc4qeDVn7]&F4XAu6+f!_HW,OK"RGcWZ,WJsZ4A695jHX*m] %['!E.@P!1HE4/TCFrQH_LeXtJWiIN/G\(?A9nP;:;O<ASJ;M`/#?YJLFCHdchKV;oW_H=NC]%'L<@_#.\#D;3@cs^?%MroUQcmiP %p\9!@S$&Pci<iU(!>8E`:.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:<JK11BaU"/Q)<a:CD/"n2*O4Z\jrhK4dp]!)h]]N02#HJP,>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<kuVJI`-)4q:Q:W_,8*dH/+'d-fU`B^gG;-)Wtc9A?H\\5`F,`p%(9c$_:FQ!fU<BV %>;O*'Wt'bL]A(B9E7;3Z!t;W,0fYQ/K,J($eUYJ7USQFr_:(=!-3d[1_-edQ_,G;9L!!rZW7.+i5?,@[6`6%>mrt<u+"EsO99jln %B5ti5Jd'28Hsbc"27r'$<%C?3M>mF\i`(M<I4`jgo%cVb20JYbRD(eN;FJdjFhB0F7>^J)%9L)`g>M:&h<ch;JS_6<USHqjb@D"R %;TfjHDej/t#$T&JXgWuho%'e)h8o3r@4pgoEH97FM*@N\n^A#*<:YfjHUZOMKpG<ffSd=nrmC:9b\1;<^!g\4l=ur[;@QQ+p#Ir] %a8-Ae8R3ZQ?tPm=Spf/3=AO)!KhD$\W@=*8/RLS"NQ/']MgMANbdDgSJZtD77peDZ*?@51Y?j4n0=m^+I$+15?TBtiTf4V)l[*;; %9pO;*=JL5@<0M#r+?MQ72JO^hD.'\RK_/6A3+oVhd7UrI(dS\PPTe/2dIOM6'rDl`^:^Z?6CIeS%\`[-B;hP>k%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@B30Z<gTl1Xpj0.+Xuin"ccCdoZ+f&ab[<:'nq#5WkD&[oHh9^%cdoCMTk"%]4nb:WhrDV< %qtKb)4F\Mi.(dMYm2Uni%sIV,lZPG'YC#hsdll[cMs#\5j7`#W%4@_u8QUV;%Jqc;DmF^b*M/Wlau\*pl&d>P9gCdq-hh`oN9mWH %]mZZA'<E4(iloEYF^3b2Np$9t9l.o>hg486[_9LXB<AQ4CupLgF[tg59\n8eQJM5hFrD@Hdb^2$`K'W!W,L[So1\8gL>YI>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_<ao-XP(/#5()m"Au>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+U<i9/FPd^$udA]@9H-]Db$tOSISNCKW`RZcZYq0#/p0A0[SK)\TZ9#Di/mRUUd:r+FqCC[&]a^N!0A::" %3jA;11YJ<?Y^/2C42Z--dl[*peihIG\ZX54l.pF)gNC5_pStq,r0=uW=J+;]onQg2G.P#Zdt>V3E&->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_Q<cYo[u %SW16C&(\YPD`t^XHOk;2]:i/:p:9@OkQfi\40;L@m#k*kp2#;&WJ>W@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<f54pi\S!0^DPH2l/:e8cW%>c %4f&hF5BuUL4a*^QgEY:cB@fqfHjZZnhhDnB@0JQ*l!5sldOIF^$lbcCOaU*I?+U_,4tVhsIBqt`nG&^EkEDf<Wuj?k:HmP(m:[dF %bf5rEH^kO@'saD<&(A:i]Z4a^G5oq)V&_.[%]h5j*Gk7&3k4/,cI4YAYtH%.NuQq>]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&6pSis<g-iMQLH4gP,$,4XVG&?XNhK!*'R1$]p %7o#bf\S'O^pu"3OXL"I<>M`<2hW!6jHh*"LQVJ(bS3"(W/<Qm&;b)VVrV>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<`7V4k<K5H@t7Dd:b[5:d'7Y7arh5=qt% %ht(29Krl4qp/XP2lD+l6VlQG?+W-e;:Y\QUO+(Y!`:Wbo5pEo6kieO2mlq<,Zm8`F-Wm8BY`<sS0"oUFCW_J,*HYpXNa.g_:W)#q %]rges44/I0OlT#*nNPlc%1SREr]Z<=p.hYCVHkW70qcQ-$h2,e'h]A'M2#AU["iXnnSMSP@#QD)LIuuJn\h?lcoF2EVdD4$BeJHr %jsEC`dAeRHm=go"OY&gY:1NWOEUS\e=X/D6GL#Z`ZHEGA5(L-/-[<TLd'\8?<-u!?]&q=+9;!:RA2q=aB\K$lE0=IChCVR@>Y^-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<iF6CFgYP`Ui0jUo-*($VC'.TuHehHL1)A#QN6>_4,OV]G0#rkH@>*nVo30799:"I-DQS`D%<b?cas. %O.-qnC;-e6e>Q=S*$^?I6Yb,>1tcY73M*.Tj(DaK0o&1O>(/!<YTe)`[K?I4SFE.i(/SlJ@?U.O'hu<UZe3r;?;aRLg8Yn9kX;1p %=e!Gn/lZm5?t;bg1G6amrA1efgcDaPcu,7NW8ceXqE!?_?:q(0GRIJ_J\Ba#k'gt\e#_42.&1$S'ACP,!UHBZ*``VehA1@nBhH`+ %BGb8fcA+[nU'bQN7_p2Ak"'u#M?m2I)0m0$`1^I^Q+WeL,=S1,EE66%i)hu0Fu\QV'oH$<pV^<-DR]rNH,nU)*AI7e@>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<c1:!OMPp(Z?BH%],Q?9ME5\oW575*l-AE4#4g>)a$OGe\*r/r49Gni*R;L2jV\G+0i132 %<MUnoBihb0CGD13ld+X/&hO9d8b3q\1pSr6dWlLRF%(-C;k:D2BWh8K=1BNE.jWt/7TfbAp;FNXj,)Q`6'I$3b/hm-B?G"GjpPgC %V14YuX%CEIioW[]A+<+#p_]%=2U%RGBfLAd/K6lP0Mp+-ku[O'BoJ-%-SA7',QXOF,t3uMY#ODE>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_<X=dL.BqQ,sR*(h&Bc0K(:@hMe %`d$&#_@]]$FJnFTfuX7Mq3Gl"^JH[e=fF,*Do8\i@!KiL_b-%#".4pX_5^r*E!Ne<U6Vk70n1"=_3;Wp\?g\$E\?n+fF2]<.]bA] %[$NU-#cd\H2EI\%Tb07)d^-]L@sO8>\#+skE@gX2k<*C%kP8GE[</:dE9q.k-[JPEe>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#O<CpI=Kq+Q[3nL3r/qj2@0mB4Gk,_Bhs01E3P&iE^:'8Eem"alKt"hFe02gDA`` %EQYtpd>KbX5'oGG-FB"VQa?N")R<a^*(7k5'L]QgJOmSIilR_`d?&Rhj.>%[4+eD;PDo.r6p\1!UG#q@`,%\["lu%k&.N,nK-r_! %\s\')`U#,CmU].A#Yg%`k<ZJE3>UJ]0DEXAnZ$.1,D;r1--CR1#`[$uYXHS:d8]_Z0-U^90oRu>(+VCLn\B"W#gR1fR1<KDadXE0 %(lspTE_423>j!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<M-1R9R/Np*$/hOC) %Db0"9!SorEI3^DbE-3d<fIh"+inR=re<Q-8B:RJ?_X<+oo>^+(,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)3Y<RW/r1@PYU[d^HBA7JL&o$j."gH+i7!.8:e0,G4jlW72//uj"`B8PCBL7 %OLt(V2.jIb28Zj'We<K1*%T0+a>V!gcWSt(KC?B:0$5N$Tj!VO0Quocb'52J(YPpj=d+ak"1fTA^GP6eXWd`ZM?4;d<<RP.-e7I; %"/rMAdho8?G;.h`EFPSi!NY69Nd0UZU^MF-pdu:,Y:^PQ]QoLm<-p!pQ4\]f5#EZ]E)3V;MD,uIb8R-T,r6)b"\bNGW+ArWqhXGZ %Ut^ML@IVffB;8'NCD7s)O@J-`C71>@N<cFo5a"K,/^*3+Y&D%*Y,JLL:,YE?`qLmhgt=q!o8#@M@[=*!k%tsCC0@R.927'+NI7ht %_c+alm1m#K1#ko"*4632qbV=a-i8+SUmE?n=D]rB.f6DM/+S&!)O6T0^$W`1Ie0gj^UI4:pZ:C7kL1GrW$`)@L2_Ne_=Q_sI;k@7 %"!OSMK+m`aoD?[W+/dOe.dBN#bnc/1IN%6XB"lrECt'!H.IhCBR0BXsPSBPt@aQE_)8RdK'eTloq4kPcP?_]&40/\n`=4:)_MWkR %19"\Qj'J8N=#OS7723B.>PK*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<YN0_riEkX7/4W;LpMCFhVaK`5NUcSbL^3+f3p'B3M3msr %?$Kl,1#J@:?@/rs!ZP\GZ(@>+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><H:Di(gqE02U4 %#,+0E>9]cC^d(k;Gn'G+<KO<#5kl+TKbrFX4#VnnYj;_6mKs&m,[/Cj.jr_BCe%;1:DH["[Hs'.=a^GP1Sm9#_]lYPo'P@l%k0%k %c@2eB;Xeb4@[&I"A1?@e*)iJnOfl'HM%nadNZo)SCuGV073P6iR<KXFPc?"iUI+MkPKuL!pP5Sg00B'OrAu9*Er236BD5$JO:2Gs %no3qPb0[3,Y#3,fDD<QB-ai9MI]sHI6KK'UJAF[mCF"Y;Lu8=-G!ZDL1J[NIT'MaUI*AQ6."cj\QVG6s8n+3RP'3=*?!eMV>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<Joj'jlq1WN=g %p-Ufo-[L#a9PuTOi3'`BdoSY%H'2/(aKe3>-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\<X=9cmu?gQS@tf9K4j(E_T_iQO=6VC:s9R!]QD.cAL3mXiGrW]CSAXP %d"<AT)BWnX6;4@WCinL_,llAq.W$KI=NSEJX*</1?Qb%EdL@&!/TJRsI3l>\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]]<PV(M4!1rOA.lqW[':as %UKj]-;;Xu*9Z^06W5TGo-GWr5H'(Z<,CldlbX&!Jj,n5]Dk&\)4?9D+49R55a4#cfA/.C.[W^i8M?%GT[+:QU`LH_@,uktCTEnl, %YAcWgV=?^K2\_2B^5*ie]Z-137_nb/d.9@YSX6.NQM7h']qBpVpdhe$*(>8@iqb->@&W0#MA&o?%s0tYRQ7'F^4m>NN*6F%o09+= %)?MR!;9P]ZP=k?+)qFLJKET,X+/G2-]&J?':EjC"WFb;^X$_=;[]*<NfFc*N[<aG.AA+_'2*rr'9.fnHl>`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&ur<ER-6Q-*J %<1fllO$0H<>l+dOn;f]1>&EB*!fU%>TPB<WU;4(Fo:ThYil#.9mQoC5!:h-OLA/-/W^Y7Gm+&*!EoA!;XU;t(G>P^<;IRUtI8DdZ %AGQ.jCAE0Kgr1Xd:pk>Wjh1n!9/'39Ui2O;j4>.SHZigTbJ`3Z/#b=JU_%_%%(lk:.!5hmE-ne)ZQG_!Ob%.T`9f0PP^[$?4<C#P %HTHdWAa+>V[[J&7$p=D&N]j7NXpCPG:KE^4B0W'S"p:HV5n]WQqr$-Ei+tE6h&*M^!G<KMP^[.aKhHBnbXk9=i7Za?MKcnC0d:A< %=WVs82?l.1hj$.<ZSA8Glr`c9g"<3ghZ_>lQp`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<h0u=KP+d[NMYq:<5Psr^tB'CiGd`.(uXcPWgfL9ff*tG %-%]#,S?9/*0OVp<>$?PMPY1SELnmtWmHsE099LRc`p\Xp@j:7-KP)ffZ\>g<E3'^mi?7hbV)n(8cH_a@d4IID)]u,0mHeYjo>InB %j)T`CKX&B?:Z^9_8sT+23BN_^aO6u!h`MijB4k^ud_"i3Cafbc)NG`ZUq%>Fm+LT0G`>rBJ`mA.NVr_oid0n8cBdhIM9S:J6O(</ %_UerH%_I,q0S]dNrqtXs`aq3=4r?#7&VYQ3@f-ihqA4s-H&b$#1&c\5LDYheg^],6+i]3n=+>uBa#VQ*I`/`cU#>,hM"/XqU;&sP %T+H1/@,ja#`d+A!&9FW&Jf)8q$p/Tfn`q0A'Oi'5-f9rT_F9EAF0C(G(mdYA59)D<hi$D6`:u>BmB7)]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 %%b2cC<I*R_,K;$U7/R'p*$L(%O$"BG/F(8!`\g6M,OQY3(^FP2@E/<W0eSk3GrT9'c\u7e'lQ?rUUiYB,OQY-(dIEdUbhdiLiJ,@ %qZe:a<V7HG))3bmJ*]4*r/siW&E=<@%aX)4.s*b3%0q7[cd:YKI?7rgKb!;7dhr)p*I`DfO$\WnpM#S9LCWL`[[/@i57S^ZA&ETA %^uNi$4E5#30L$BpNea^N(EG+pf)H=C>S1-=&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(<Ao %)s5*=$6?:&id;.qr6,uS1NYTW?Np22i9OWZV''Zk%:#8j()0;QN5lQfj[B(J?`Nq$rW(<Hm,"@G*X1WDm&Im;HV"SUN:.#;"Q2K6 %#pWOC;U_DFPc3_6g_F$.Pk0K:.\WAQ*5h/fU!0dbcG(iTG+B(:eqI0o'$;KnYf`)mcV'T><q:B]Z@#OV,!GO(77>tY`D%u4Khc$r %`Hu(Hr,AGJ[6&?'B/[MXV_2oPa3F6WcI1$rr:ITs@8:+Dct"Cd*np@cTVH<NE'W96;g_Bf&NnC<ia`hDTOV"\k]jcN<dlru<,ENW %0.D6P9?M%kHARfhqi1Y@.7B0c&Y0TL0$;qp)(9P9mhH%5.GU5#TWAS)_<X_4@Fu!ADh6hh-%CO,SV2`^W#4@8Y$g/"L@?)dfmjlk %?07qps+[Phgquqdb<>Pe"!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@<at+Ggi47pKs3j,)P[qB(f0=$f=5JV,A6ibEOb-(7M-3CIb_Vg%.?n<m1+G%%3@?n %:i%]%/%`aq;j\_Gr=Rj0=Od3C(;@2W#"#HH+1K8>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<O^*ejun.$spp>%9$c7LXD' %\=?`?n7l)l>%4MncK;]A<hWk)\gC0O;bAXboH=7AO1Xk"e2"nS%Nc7'JS^NMbEoQ=G^)0UV9a'C8X\@YQs,76CS6t8cpa-eX^>)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<t#-GMTMAL[TV.u1] %#V3>/lRd&C5)c0#p_k%=\8rIpH%!X%aJ^]4d\7[!/tDD/(/CB,NAns0/Qcpon2]U\<d-K$BFi)lj4_$TkMGNO`$P:EN/Y=6qbA/S %\;2BjA"f2,\jf?`GN;kgrl(QZdK/Q3e7'&0-Xo;Y*QDi?3sq;dBqhM@MhfM8g4e!<"##L$hK-;"I!bmY^R`--`G0BT.D/N9OOfVC %D3QDA/Ce`lnMSOLV+cu-%J=Fia;!2G3*hN))#2$I,$HK=+?hh"EbJ1&<.eq!cSnp^*^p:ne>SuhD7@HDe66M>ZHH[8R(SEQTC`A) %]<A;OFVneN:ROd&ClNo]g,a(oDpa_A`MI?u"sSe:6VeYNI]J1#ak,5YW"q;)i6Sne9DnEh^[dS'3;hLPD!G$H3^9`qQ04)"[&\GM %pX0guYEuV!^0CU*[q3YsSSF+!><B+I)QW"umFH("l;j+6bNLj374moDW,<!tMWU7b@9JBtrQ6q\Nl?+pD*1_E>.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*<L]ji[2DmM2gE %TGL3lIT'p=a)03Uo'5%*/G?@0=5^1ak%aW^K"5Qb4m0^2okZA?X^1(^[=<T`@D@`sB1cqm2O[gUA"\H[/opGW<9k[?-*>'rqLm>i %U36C"4'LC[)2@MIPEf>7.5c3;KBV")L=\S#dFdqqdE-%c$V900)2q00KAD&6dk3f:DXHM*<S2k"2)asXQq:1Z?/f'a/s>gWbN!d7 %qBgO`epA]1p@ZRoW])`lk,&X$cdmm<cP!q8f^AH\lAa<aXW(`4jC+-Pml\MJM4ZGPOZbQ?l)feUmK[S*I"%-H-9#qslP!M$GY:%` %Mc[CNaYG*AUSe2s[YF).C?7!36e)`]jaZlYaBS3seT]72M-/PGUo?\/"NM/)(n#jAD,&UF2?B,UrL'7g>Hk5"fRq0N5P>bqJ9D=0 %L#mDn)7(Rq<GM($jF@bfLGS1mIPqc_<@/.Ub."j.0]lBm6Ve^EDb0HnI:,p3p[C0pHJ33=WC::TKhk,4+Dp]8@s!M@D;Gf5%e>F# %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(!<cMle><mfX;8E"TT:q1WKB@+3#gp/!FACsU<jKJ,P>!:\A1/hCgeuK,a*$:P\Pjgg>=6m6>@7&'O_##N.;q%^A!i\;Qas3 %ULt?I!G[gE_1;+DoL_CH1UG2r=T_Un;,1p<bS+(eXh*8feV.]"gD>hr&_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?#<qN_<$YW:"67MY",CF;A(V;%bUYtF0a@3&n.]-T$7M7T8p%u"/^!ncnO;L`mOQirCP])m<q6lI-Y("'H$;T]Yk9;ak:%2 %?g;;USaLPXT5r=8LNuFF!^S2$[q^*%[buq1fLO&&aZ+'*F4@/)`-:GYe$$5O?qf*uE(a&=eR9jQMpK.0:0NS`4/uP7H`'?JLJMeo %6GKje2`3-FTe2XU#3(2G3gm#Cp=p;%[hd)*_qq&aj]rB(iHHX1fc"Q$?irpGG0mBXbS6KD5.q'0r+:8\Mu,KjBmjD8m=B-=,mo!r %B<Ogu)-[lf%7f=7A>hB$\r;C%e<O"Ho[pe9oNF+*AWgE:4;X?<EXYKOV?Z^"3:Qne8WrP>BHQ%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(X<f\J_I&>P^,1,36u %5-s6qG(md`f9EB)f*=(g[Y3#e6e>^a>BmRHqpS+G:"EY5\`7P27e#0P18<V8:M)#l1A(3;pQO!+dS:q>3t9gPBZ$3Yhe'ZkQ(*-: %To<"pfX#J-b6'*h"["8.!*s<.ch'JK5NT]YDG7inBpm)c@Vni!IPtVBpQ,m[W]<sN?o-Hi5j0S;BU,6f6I1sPD2oHcL5UFDGUS^g %`O%em:FD&i:[+5\D$c*f'Se)![CI0:jSf2bOm\-J6tcZQ*`%99.lbq.#?5eUq/u7&qIYKp=oq7cZgnuW#mdZsSFsc"*\ge*CqUDa %F>[TYf[p#!`9_?P_pJ+-9qXB(PurYnS%A4@YaRePC]u2Fhblp):\DD/=*DT<)4GtueH5q>F:V(&2f3fUN&oA.]4F`s2S(If0+>E4 %@k#YI[[1AbPO/<G<dN1Y[$feMCQXE,`kgmI*=sXT(F2:CT;u*"=CHK+%0M!L5`hZUG#nRXND0TMq>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<Z>?nYR2]H6oS5; %+.,\=b$QgjU"&<op1BD(Ji5bd/c8*11G=W>5lW.uIQ^P/j9$R<CW=[!Tj2<+g8mi=o2Gi.hM?<9pb0DkR?hg,=L]74:8YhPDrrps %j>V5NZ`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;%.WD<du6bI)tfmC_?,*_kGED(K"I&Ol2R %C#4J1U?I$@HSsL>WDfml--[)[%%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#'N6S<h1d,b9(Ks3,-TUT'KI"4<97Ndod %5p0Fr0;Q&7:,PC5A;>WN\*2GAhjPC'3+,<<Co]G^)8#&)#aD0&&"&\sIZO_Jdb0At\Eo[!rRt]u\lY\ucE'1oN_d(0pC"=-P?<3M %;kp&@5Yn;S0="VCKTDuG]q]X,X.Jl>#l*af2d+u8<gh:5lMu;eoGhBo*Vp?Yptg#kMBLLRNmFbk/1mnY-98(ONu\tQ0.GZAC;d-j %ZYu?&>9Z(WVJOZ%Vu8FI(!)CY21COXF5q8Z#`+@rLcQb0iOqk3-VooF-p^g*QDgi0-"KTq(r:eF]<2arq@II"Tgq1jEtatN"3lr" %#l:W6VGE;&Y.Z(qGQYrbBuQIGK-nrSkH>%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?uU<T8:`&\mp2/ %it2u/SE1H%ChNjhLVNt;Z>c`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$_>R<a@3B* %<l%][rRpBgZ(ZIj'GlAl4pp**n8E(*[M-]0"Zc$dDqQlL@asuQg4Ac%9^33DEG24^[dk\(#o-l2q-F"/=)Bm`LjhC8,-5p]CjbS, %l2$(LU&?tp.!!;B7$nW^Xf.-J<(]/>F@G>a\H/^c=kfWG><:'GZ_"79l]QOYB\kGed$&<oKbb6T+@Q(Z&K)X*rB>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<R)NHuuB0L.>>:sGAe;?!;6&,&2Vr)>1'cBj714W4(tSrqFsC3sZ;XmTHJjp1p$R#gp^ %'&*u;;'QG&n<dH_*7shJ%4C$0P$?43XDY]=geWV)#1'ITbbWE5b[Z#j;(WakpYn,h[K^hIYKU*#+V1RYi)VT`r]+fr'Gs^Y5j5tg %3k(W>l<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<\<p1aLT>)&BMd5YWR4U\Dt]H4QL?[I&fA1TG*JKXJ[)!NZZ(A$_GBgN0_3goars]4tdE %"n`Q%Qqo"c^u<lEm-'E(="btD6HVO[X1,&nE9?5C.0BDMP5P/CV8X^ZYSBdW%lPcW-?A&H3=j@BNsal_O7eoEIW[HRhu=(kfU-!X %*Rb'N5?fsb/9osURjOVaf>.g]4sd*[B`g&ZbZVC.U6aQc;'LQ#dN=C"2Uq<b`IF+c"L'MGJ]%MOr3Ea'a/Th_$*NB4E13YUbh%.& %nsD=2l+e:+k<Np$B.15$$I)q)R?qn@ad5,0-a<_/W+A8I92;+o8hfSLesODB#J-$K-9eeQo(,hL&tRMfNDnP2&G?;XW&6WTDkV_u %GQMBK^Sh%[aJu@_1S"nQ;\#qirV4cE@Ea#+Oj8Do;TES?MD,kU?B:&$A7>(I8<!Zd5?7(%B7-fqXGF*2,uUMq%<Qd@m.'&mo.m9l %q4"X,:;g)\:Z6ZDVPgssZGqc*N5l^><c;0W9NDG5d-7X?#:45Ne*]$`fsQ25T#4>6DnMCqe(<Um&6'S$8cQ<3!A*[YNn:oZQq_fu %rdoi$J*-'o;FAp]#]FaRY$'20Qa._C)RCC2he4%#b*Lsa11mtRLoH\p[+k*3j_K4sTo@i"K]2:4o#Ul?!E5k5K8/nL9_h+PC[jXn %aFK9+iiF_5mPn,<g?9DHp3gu0an<GM0S$D6$8$YQcOBh.<9m?ilGqmI(&Tr6bib34ZbR\3;N$U`RTg3E@DSM(eS)EhiZFc`^?Gc^ %WrZ8bRgu:Bi)'bQ@AF(.U<rK#OeoD-V-`@gHj3p(r1[DIdErnX9&QJiee0ip&IW%P>[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;S<J8BjV^%r/JWPh\Zs.7)4m(=hq?/%_?eO)t"&)gkL?VX.a7B-QWD %)n<QJo1MK=q?;dC@uLkodg6_k#HgV\U++g\"?<QW8Gu1A^!FGo`CipP?kjLe&e<@D^FpHOl#&R>K=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<K$ %/4!qa=PSSI]I2V-],[4:@`)OSZ995j%/T!c<5Ht"Z]7,QAft%.?i'_kSldL(MBEZ\QEs(;Y'QdDFeqhWZ?uV'oe9aFV/uT=J<bK- %jR7'7k;<O7h^9Gd0j-8jMJQQ?9@mV`[)ueH%Fdd=J(.bA"6o]!(XRS#HmLml.^;Pb@beM4m%&!XlOIq_>-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\$X<E$nLP3e\/LFn-5e(r20XfU798)Y:/-3O6*ia254q"7F.iADJT@XZVg$a3Y:qjC85h=#AQ.M9sk1!e3hbu`=Cb;SIn[lY* %HLdX(!Q,opfkZ.\kUTdaN&O]bl"MLO_tsZ]WPhk(CH7?rqk-XG@OjCi8>Ha^Q`o$+LmmLa7;.BPlrTdSEK_;2?C#!jGq-1:O:(QC %6''*OW&_s+>\Jb"0^Vh/FSKOd)VPL_WEHsF$IrYbOn4+1jsa3J<ZM,-M^B8Jq>Z0HU#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-;"*<V]1VBl60=q5,@CQVUI]RlWO^o#MusG.4PUK*g66iCG(_[mp8>K=Mr7^g-22,;qk(%9t-NkNHlkC7oK!KJO3I- %s/j#J'C(%<'WLXuhp=fgLeIkMFsa*T[9#*skeCSlGNODK.&6jL<q,!Od71Ucel]V815!BcqVtR`"i+oD2/!?aO6cXI@Rd+)<,'JO %3l!D>]q@4u2?W>#O28]:Vs*1j:kJ05J/1ZV3!;l9/2&Yr2<J]aD`7s_NHkTQ,-Xnj2!5j?`;gbGFG>_!H/*KXQ-I*8>k2BFE[K\r %,#[KK_.Yb<CZ0##Tsd6/:HfGA/X#k<>\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\iNq3Z<de0IQa=B;hc$#S$H80&c[kts#H=k>2=J,rlgJ2'N/2_b<lG;]O9bBr^?^BI43J?r_W?63C %([W$5],R%<NFg6M3)mbY9@'H`II=`pT,DZ+)aX,9X2ss"(m':)j)2AaeK;5[Ug]$5kHY*biE:"Q8>j?O6IUQlmQD/A+83@*%*\H^ %KD/G8d9LGFS6<t^BNdYE>uWu2HR.D`BFdEFNQfN?0'5GC0$++kWN[(>p;t2:k=tZHYeoGe[<KABnFH!o-/GiDNq]<!7V6/>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<YNi:,\Gd:@;/:T%;X7D7Z5kL8W+k<_*VDUWV.S$ncQ+.* %/@O@IH^!"nHhD(nY,=<KGLBXs*K$E'SoONo:2Aaghr#;jM#A</mDW[,CYe3`?s\`H;iJq(Kk2WO[qKP$Ua'nZ=n\I43hk(lHH%4B %M2oA-o2*?.Mu,J%7sGh?7gbl6B%n/l"_E0(Snh8#^SWb/an$ZFNWNG#/;TQ(hbFY9a51HQ0#<!&5`4X$)A.TPP*<c`qABq\]-XCf %X,n5\`tRN7+V=oYoo=*j[e7"eKhkU2(3gfPM'F:aCsoOo_i(K4RG2%O.gX&>^DpO\`W=d-3J<"/4Fbr<QgUNm8>f[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<a %aen"S+\\[2!o-E(08%Uo^j38F+Ddgki^ISR<A;u0oH5].%WQUPPeAtt#q1Er<.jkUfs,=b9tl>'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#e<qFGb?QZsDNY&H^J?@uk";mEfr_FQ%8^IraGa4Y*\6+Mk2"h6n=PS)Im`R?a!kmXrP=?b/GCi5^=U4KZl_MTH.*W,dQ %L4BYh]qh`9;tRs@SS=)@WDT]l:lCk0>MrTEr6?]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?=3Y<XAJBc+08;*g?3)f:9d,k^5go01fQNf@1OR0"#Vic4[dNf#JqNM>OOEGl5%ohVb50h[+M,fb5%3h %S5;S^/?5ksA,G8N.Q&"7TWS4O3j!'H2c)#cr/a84M9oM5\@qn6*mOhTHjquZ.mai><V=LQ=7KCYf?k:/*,d)D90sFJ,mVi]Y[`fF %/G*<G>X7N/\0@tjm@GNu`G_QA;$`.3H47eP&Rssm_j[_/F<eDFdk._/JP/KRP7OCr>B`$^!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<P+56ejKPmt1d>/"IG4Z:S^-lL7Z0;n#ah@$UE-T<E\S8ler-.Ai0U3?s5,DE\hdgVSn %?&\q(n93riIn\(r\3RU"5P;TsEuJUK@t!&Ppp:9*"FZpKIOs!sAnnR]bK&5=qKis.7G\o6G'qqq\WlV8Qa6!VVW:h!CF@pkLEKc* %)C&UI#*nO'"_M9Kd38fJO8N7_THoIM/+JdCS?`d_Bl\lTX+ea[kPW&8bS\Q2"Lm73S9d'kZqdT^jQY;&dkMR=2D0)3:_DRG');<B %Hb.0L#T28&.j@g'#V<&':jrJSdf#gdWTFk%!ac)d=>KilapT1Jh%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?T<E\Kg].82^e;p]iB9j6Mu7)Rm=BVZs3-!3G7WI<^9YNU[N;*GVfD_]_>fu(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&FM4<bV]])?M;6."j@LbO;^1=kK-#6P*YNIVQ/qZ%R&DJ.T(07]/f+E_)o>R6,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%)3u<F_!;st[k-"fo"ME#V35>t4mY(_RcgCB<kLW%. %-%MV<)$<*R.kp1.qYX[9e:;,gpVEifV_80j#)e2M-QI/QHpdss5Qj7.0k%iG9=VZKG\F+ncbL6K69sUWK0_270K_Tc&F<&o=3duM %&j.5V3Zm9M!%mN0+)j"I6E^_0*k6C0&.p:O%"h>pK3&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<t/rB`bq0^^^gVG<`YS$ou-JoEg$4Fbded_UU,\*+/g[ %7Ke"QgVL#pf>%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*<Y7U.`<fkj/q%PX^73"tiEj?fS*gU6u!*;!&[hS-\qNf)U0C %Y5t1/7N<*>(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 0000000000..35beca7e09
--- /dev/null
+++ b/docs/xen-api/xenapi-coversheet.tex
@@ -0,0 +1,40 @@
+%
+% 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.
+%
+
+%% Document title
+\newcommand{\doctitle}{Xen Management API Draft}
+
+\newcommand{\coversheetlogo}{xen.eps}
+
+%% Document date
+\newcommand{\datestring}{25th August 2006}
+
+\newcommand{\releasestatement}{Open Preview Release\\Comments are welcome!}
+
+%% Document revision
+\newcommand{\revstring}{API Revision 0.4.3 (Draft for discussion)}
+
+%% Document authors
+\newcommand{\docauthors}{
+Ewan Mellor: & {\tt ewan@xensource.com} \\
+Richard Sharp: & {\tt richard.sharp@xensource.com} \\
+David Scott: & {\tt david.scott@xensource.com} \\
+Jon Harrop: & {\tt jon.harrop@xensource.com}
+}
+\newcommand{\legalnotice}{Copyright \copyright{} 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".
+}
diff --git a/docs/xen-api/xenapi-datamodel-graph.dot b/docs/xen-api/xenapi-datamodel-graph.dot
new file mode 100644
index 0000000000..794d9ea17a
--- /dev/null
+++ b/docs/xen-api/xenapi-datamodel-graph.dot
@@ -0,0 +1,17 @@
+digraph g{
+node [ shape=box ]; session [ URL="session.html" ] task [ URL="task.html" ] VM [ URL="VM.html" ] host [ URL="host.html" ] host_cpu [ URL="host_cpu.html" ] network [ URL="network.html" ] VIF [ URL="VIF.html" ] PIF [ URL="PIF.html" ] SR [ URL="SR.html" ] VDI [ URL="VDI.html" ] VBD [ URL="VBD.html" ] VTPM [ URL="VTPM.html" ] user [ URL="user.html" ] debug [ URL="debug.html" ];
+session -> host [ label="this_host(1)" ]
+session -> user [ label="this_user(1)" ]
+host -> VM [ color="blue", arrowhead="crow", arrowtail="none" ]
+host -> host_cpu [ color="blue", arrowhead="crow", arrowtail="none" ]
+VIF -> VM [ color="blue", arrowhead="none", arrowtail="crow" ]
+VIF -> network [ color="blue", arrowhead="none", arrowtail="crow" ]
+PIF -> host [ color="blue", arrowhead="none", arrowtail="crow" ]
+PIF -> network [ color="blue", arrowhead="none", arrowtail="crow" ]
+SR -> VDI [ color="blue", arrowhead="crow", arrowtail="none" ]
+VDI -> VBD [ color="blue", arrowhead="crow", arrowtail="none" ]
+VDI -> VDI [ color="blue", arrowhead="none", arrowtail="crow" ]
+VBD -> VM [ color="blue", arrowhead="none", arrowtail="crow" ]
+VTPM -> VM [ label="backend(1)" ]
+VTPM -> VM [ color="blue", 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 0000000000..8c06fb8a33
--- /dev/null
+++ b/docs/xen-api/xenapi-datamodel.tex
@@ -0,0 +1,9648 @@
+%
+% 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.
+%
+
+\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 longrunning asynchronous task \\
+{\tt VM} & A virtual machine (or 'guest') \\
+{\tt host} & A physical host \\
+{\tt host\_cpu} & A physical CPU \\
+{\tt network} & A virtual network \\
+{\tt VIF} & A virtual network interface \\
+{\tt PIF} & A physical network interface (note separate VLANs are represented as several PIFs) \\
+{\tt SR} & A storage repository \\
+{\tt VDI} & A virtual disk image \\
+{\tt VBD} & A virtual block device \\
+{\tt VTPM} & A virtual TPM device \\
+{\tt user} & A user of the system \\
+{\tt debug} & A basic class for testing \\
+\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
+VDI.VBDs & VBD.VDI & many-to-one\\
+VDI.parent & VDI.children & one-to-many\\
+VBD.VM & VM.VBDs & 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\\
+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) diagramatically, 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 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 \\
+\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 ShuttingDown} & Shutting Down \\
+\hspace{0.5cm}{\tt Unknown} & Some other unknown state \\
+\hline
+\end{longtable}
+
+\vspace{1cm}
+\begin{longtable}{|ll|}
+\hline
+{\tt enum cpu\_feature} & \\
+\hline
+\hspace{0.5cm}{\tt FPU} & Onboard FPU \\
+\hspace{0.5cm}{\tt VME} & Virtual Mode Extensions \\
+\hspace{0.5cm}{\tt DE} & Debugging Extensions \\
+\hspace{0.5cm}{\tt PSE} & Page Size Extensions \\
+\hspace{0.5cm}{\tt TSC} & Time Stamp Counter \\
+\hspace{0.5cm}{\tt MSR} & Model-Specific Registers, RDMSR, WRMSR \\
+\hspace{0.5cm}{\tt PAE} & Physical Address Extensions \\
+\hspace{0.5cm}{\tt MCE} & Machine Check Architecture \\
+\hspace{0.5cm}{\tt CX8} & CMPXCHG8 instruction \\
+\hspace{0.5cm}{\tt APIC} & Onboard APIC \\
+\hspace{0.5cm}{\tt SEP} & SYSENTER/SYSEXIT \\
+\hspace{0.5cm}{\tt MTRR} & Memory Type Range Registers \\
+\hspace{0.5cm}{\tt PGE} & Page Global Enable \\
+\hspace{0.5cm}{\tt MCA} & Machine Check Architecture \\
+\hspace{0.5cm}{\tt CMOV} & CMOV instruction (FCMOVCC and FCOMI too if FPU present) \\
+\hspace{0.5cm}{\tt PAT} & Page Attribute Table \\
+\hspace{0.5cm}{\tt PSE36} & 36-bit PSEs \\
+\hspace{0.5cm}{\tt PN} & Processor serial number \\
+\hspace{0.5cm}{\tt CLFLSH} & Supports the CLFLUSH instruction \\
+\hspace{0.5cm}{\tt DTES} & Debug Trace Store \\
+\hspace{0.5cm}{\tt ACPI} & ACPI via MSR \\
+\hspace{0.5cm}{\tt MMX} & Multimedia Extensions \\
+\hspace{0.5cm}{\tt FXSR} & FXSAVE and FXRSTOR instructions (fast save and restore \\
+\hspace{0.5cm}{\tt XMM} & Streaming SIMD Extensions \\
+\hspace{0.5cm}{\tt XMM2} & Streaming SIMD Extensions-2 \\
+\hspace{0.5cm}{\tt SELFSNOOP} & CPU self snoop \\
+\hspace{0.5cm}{\tt HT} & Hyper-Threading \\
+\hspace{0.5cm}{\tt ACC} & Automatic clock control \\
+\hspace{0.5cm}{\tt IA64} & IA-64 processor \\
+\hspace{0.5cm}{\tt SYSCALL} & SYSCALL/SYSRET \\
+\hspace{0.5cm}{\tt MP} & MP Capable. \\
+\hspace{0.5cm}{\tt NX} & Execute Disable \\
+\hspace{0.5cm}{\tt MMXEXT} & AMD MMX extensions \\
+\hspace{0.5cm}{\tt LM} & Long Mode (x86-64) \\
+\hspace{0.5cm}{\tt 3DNOWEXT} & AMD 3DNow! extensions \\
+\hspace{0.5cm}{\tt 3DNOW} & 3DNow! \\
+\hspace{0.5cm}{\tt RECOVERY} & CPU in recovery mode \\
+\hspace{0.5cm}{\tt LONGRUN} & Longrun power control \\
+\hspace{0.5cm}{\tt LRTI} & LongRun table interface \\
+\hspace{0.5cm}{\tt CXMMX} & Cyrix MMX extensions \\
+\hspace{0.5cm}{\tt K6\_MTRR} & AMD K6 nonstandard MTRRs \\
+\hspace{0.5cm}{\tt CYRIX\_ARR} & Cyrix ARRs (= MTRRs) \\
+\hspace{0.5cm}{\tt CENTAUR\_MCR} & Centaur MCRs (= MTRRs) \\
+\hspace{0.5cm}{\tt K8} & Opteron, Athlon64 \\
+\hspace{0.5cm}{\tt K7} & Athlon \\
+\hspace{0.5cm}{\tt P3} & P3 \\
+\hspace{0.5cm}{\tt P4} & P4 \\
+\hspace{0.5cm}{\tt CONSTANT\_TSC} & TSC ticks at a constant rate \\
+\hspace{0.5cm}{\tt FXSAVE\_LEAK} & FXSAVE leaks FOP/FIP/FOP \\
+\hspace{0.5cm}{\tt XMM3} & Streaming SIMD Extensions-3 \\
+\hspace{0.5cm}{\tt MWAIT} & Monitor/Mwait support \\
+\hspace{0.5cm}{\tt DSCPL} & CPL Qualified Debug Store \\
+\hspace{0.5cm}{\tt EST} & Enhanced SpeedStep \\
+\hspace{0.5cm}{\tt TM2} & Thermal Monitor 2 \\
+\hspace{0.5cm}{\tt CID} & Context ID \\
+\hspace{0.5cm}{\tt CX16} & CMPXCHG16B \\
+\hspace{0.5cm}{\tt XTPR} & Send Task Priority Messages \\
+\hspace{0.5cm}{\tt XSTORE} & on-CPU RNG present (xstore insn) \\
+\hspace{0.5cm}{\tt XSTORE\_EN} & on-CPU RNG enabled \\
+\hspace{0.5cm}{\tt XCRYPT} & on-CPU crypto (xcrypt insn) \\
+\hspace{0.5cm}{\tt XCRYPT\_EN} & on-CPU crypto enabled \\
+\hspace{0.5cm}{\tt LAHF\_LM} & LAHF/SAHF in long mode \\
+\hspace{0.5cm}{\tt CMP\_LEGACY} & If yes HyperThreading not valid \\
+\hspace{0.5cm}{\tt VMX} & VMX instruction set \\
+\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 boot\_type} & \\
+\hline
+\hspace{0.5cm}{\tt bios} & boot an HVM guest using an emulated BIOS \\
+\hspace{0.5cm}{\tt grub} & boot from inside the machine using grub \\
+\hspace{0.5cm}{\tt kernel\_external} & boot from an external kernel \\
+\hspace{0.5cm}{\tt kernel\_internal} & boot from a kernel inside the guest filesystem \\
+\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 driver\_type} & \\
+\hline
+\hspace{0.5cm}{\tt ioemu} & use hardware emulation \\
+\hspace{0.5cm}{\tt paravirtualised} & use paravirtualised driver \\
+\hline
+\end{longtable}
+
+\vspace{1cm}
+
+\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{ins}$ & {\tt this\_host} & host ref & Currently connected host \\
+$\mathit{RO}_\mathit{ins}$ & {\tt this\_user} & user ref & Currently connected user \\
+\hline
+\end{longtable}
+\subsection{Additional 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}
+\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\_this\_host}
+
+{\bf Overview:}
+get accessor message derived from field this\_host of object 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 & object instance \\ \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 accessor message derived from field this\_user of object 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 & object instance \\ \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:~create}
+
+{\bf Overview:}
+constructor for class session
+
+ \noindent {\bf Signature:}
+\begin{verbatim} (session ref) create (session_id s, session 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 session record } & args & All constructor arguments \\ \hline
+
+\end{tabular}
+
+\vspace{0.3cm}
+
+ \noindent {\bf Return Type:}
+{\tt
+session ref
+}
+
+
+reference to the newly created object
+\vspace{0.3cm}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\subsubsection{RPC name:~destroy}
+
+{\bf Overview:}
+destructor for class session
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void destroy (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 & object instance \\ \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:}
+returns the session instance with a particular 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:}
+returns a record containing the state of an instance of class 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 longrunning asynchronous task}} \\
+\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 \\
+\hline
+\end{longtable}
+\subsection{Additional RPCs associated with class: task}
+\subsubsection{RPC name:~get\_status}
+
+{\bf Overview:}
+Poll a running asynchronous RPC invocation and query its status
+
+ \noindent {\bf Signature:}
+\begin{verbatim} (uuid ref) get_status (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 ID of the RPC call to poll \\ \hline
+
+\end{tabular}
+
+\vspace{0.3cm}
+
+ \noindent {\bf Return Type:}
+{\tt
+uuid ref
+}
+
+
+String describing status of specified asynchronous RPC invocation, including estimated completion time
+\vspace{0.3cm}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\subsubsection{RPC name:~get\_all\_tasks}
+
+{\bf Overview:}
+List all asynchronous RPC calls currently executing
+
+ \noindent {\bf Signature:}
+\begin{verbatim} ((task ref) Set) get_all_tasks (session_id s)\end{verbatim}
+
+
+\vspace{0.3cm}
+
+ \noindent {\bf Return Type:}
+{\tt
+(task ref) Set
+}
+
+
+A list of tasks currently executing. Note that
+tasks are associated with users rather than sessions. Thus, if you logout and
+login again with a different session but the same user, this function will still
+return the user's running tasks.
+\vspace{0.3cm}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\subsubsection{RPC name:~get\_uuid}
+
+{\bf Overview:}
+get accessor message derived from field uuid of object 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 & object instance \\ \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 accessor message derived from field name/label of object 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 & object instance \\ \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 accessor message derived from field name/label of object task
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_name_label (session_id s, task 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 task ref } & self & object instance \\ \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 accessor message derived from field name/description of object 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 & object instance \\ \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 accessor message derived from field name/description of object task
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_name_description (session_id s, task 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 task ref } & self & object instance \\ \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:}
+constructor for class task
+
+ \noindent {\bf Signature:}
+\begin{verbatim} (task ref) create (session_id s, task 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 task record } & args & All constructor arguments \\ \hline
+
+\end{tabular}
+
+\vspace{0.3cm}
+
+ \noindent {\bf Return Type:}
+{\tt
+task ref
+}
+
+
+reference to the newly created object
+\vspace{0.3cm}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\subsubsection{RPC name:~destroy}
+
+{\bf Overview:}
+destructor for class task
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void destroy (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 & object instance \\ \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:}
+returns the task instance with a particular 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:}
+returns a record containing the state of an instance of class 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:}
+returns the task instance with a particular name 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: 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{RO}_\mathit{run}$ & {\tt resident\_on} & host ref & the host the VM is currently resident on \\
+$\mathit{RO}_\mathit{ins}$ & {\tt memory/static\_max} & int & Statically-set (i.e. absolute) maximum \\
+$\mathit{RW}$ & {\tt memory/dynamic\_max} & int & Dynamic maximum \\
+$\mathit{RO}_\mathit{run}$ & {\tt memory/actual} & int & Guest's actual usage \\
+$\mathit{RW}$ & {\tt memory/dynamic\_min} & int & Dynamic minimum \\
+$\mathit{RO}_\mathit{ins}$ & {\tt memory/static\_min} & int & Statically-set (i.e. absolute) mininum \\
+$\mathit{RW}$ & {\tt VCPUs/policy} & string & the name of the VCPU scheduling policy to be applied \\
+$\mathit{RW}$ & {\tt VCPUs/params} & string & string-encoded parameters passed to selected VCPU policy \\
+$\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{ins}$ & {\tt VCPUs/features/required} & (cpu\_feature) Set & CPU features the guest demands the host supports \\
+$\mathit{RO}_\mathit{ins}$ & {\tt VCPUs/features/can\_use} & (cpu\_feature) Set & CPU features the guest can use if available \\
+$\mathit{RW}$ & {\tt VCPUs/features/force\_on} & (cpu\_feature) Set & CPU features to expose to the guest above the bare minimum \\
+$\mathit{RW}$ & {\tt VCPUs/features/force\_off} & (cpu\_feature) Set & CPU features to hide to the guest \\
+$\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\_suspend} & on\_normal\_exit & action to take after the guest has suspended itself \\
+$\mathit{RW}$ & {\tt actions/after\_crash} & on\_crash\_behaviour & action to take if the guest crashes \\
+$\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 VTPMs} & (VTPM ref) Set & virtual TPMs \\
+$\mathit{RW}$ & {\tt bios/boot} & string & device to boot the guest from \\
+$\mathit{RW}$ & {\tt platform/std\_VGA} & bool & emulate standard VGA instead of cirrus logic \\
+$\mathit{RW}$ & {\tt platform/serial} & string & redirect serial port to pty \\
+$\mathit{RW}$ & {\tt platform/localtime} & bool & set RTC to local time \\
+$\mathit{RW}$ & {\tt platform/clock\_offset} & bool & timeshift applied to guest's clock \\
+$\mathit{RW}$ & {\tt platform/enable\_audio} & bool & emulate audio \\
+$\mathit{RW}$ & {\tt builder} & string & domain builder to use \\
+$\mathit{RW}$ & {\tt boot\_method} & boot\_type & select how this machine should boot \\
+$\mathit{RW}$ & {\tt kernel/kernel} & string & path to kernel e.g. /boot/vmlinuz \\
+$\mathit{RW}$ & {\tt kernel/initrd} & string & path to the initrd e.g. /boot/initrd.img \\
+$\mathit{RW}$ & {\tt kernel/args} & string & extra kernel command-line arguments \\
+$\mathit{RW}$ & {\tt grub/cmdline} & string & grub command-line \\
+$\mathit{RO}_\mathit{ins}$ & {\tt PCI\_bus} & string & PCI bus path for pass-through devices \\
+$\mathit{RO}_\mathit{run}$ & {\tt tools\_version} & (string $\rightarrow$ string) Map & versions of installed paravirtualised drivers \\
+$\mathit{RW}$ & {\tt otherConfig} & (string $\rightarrow$ string) Map & additional configuration \\
+\hline
+\end{longtable}
+\subsection{Additional 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}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\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}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\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}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\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 pause \\ \hline
+
+\end{tabular}
+
+\vspace{0.3cm}
+
+ \noindent {\bf Return Type:}
+{\tt
+void
+}
+
+
+
+\vspace{0.3cm}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\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.
+
+ \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}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\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.
+
+ \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}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\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}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\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.
+
+ \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 hibernate \\ \hline
+
+\end{tabular}
+
+\vspace{0.3cm}
+
+ \noindent {\bf Return Type:}
+{\tt
+void
+}
+
+
+
+\vspace{0.3cm}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\subsubsection{RPC name:~resume}
+
+{\bf Overview:}
+Awaken the specified VM and resume it.
+
+ \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 unhibernate \\ \hline
+
+{\tt bool } & start\_paused & Unhibernate VM in paused state if set to true. \\ \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 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 accessor message derived from field uuid of object 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 & object instance \\ \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 accessor message derived from field power\_state of object 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 & object instance \\ \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 accessor message derived from field name/label of object 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 & object instance \\ \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 accessor message derived from field name/label of object 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 & object instance \\ \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 accessor message derived from field name/description of object 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 & object instance \\ \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 accessor message derived from field name/description of object 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 & object instance \\ \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 accessor message derived from field user\_version of object 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 & object instance \\ \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 accessor message derived from field user\_version of object 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 & object instance \\ \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 accessor message derived from field is\_a\_template of object 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 & object instance \\ \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 accessor message derived from field is\_a\_template of object 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 & object instance \\ \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\_resident\_on}
+
+{\bf Overview:}
+get accessor message derived from field resident\_on of object 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 & object instance \\ \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 accessor message derived from field memory/static\_max of object 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 & object instance \\ \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\_dynamic\_max}
+
+{\bf Overview:}
+get accessor message derived from field memory/dynamic\_max of object 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 & object instance \\ \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 accessor message derived from field memory/dynamic\_max of object 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 & object instance \\ \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\_actual}
+
+{\bf Overview:}
+get accessor message derived from field memory/actual of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} int get_memory_actual (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 & object instance \\ \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\_dynamic\_min}
+
+{\bf Overview:}
+get accessor message derived from field memory/dynamic\_min of object 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 & object instance \\ \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 accessor message derived from field memory/dynamic\_min of object 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 & object instance \\ \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 accessor message derived from field memory/static\_min of object 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 & object instance \\ \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\_policy}
+
+{\bf Overview:}
+get accessor message derived from field VCPUs/policy of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} string get_VCPUs_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 & object instance \\ \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\_VCPUs\_policy}
+
+{\bf Overview:}
+set accessor message derived from field VCPUs/policy of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_VCPUs_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 & object instance \\ \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\_VCPUs\_params}
+
+{\bf Overview:}
+get accessor message derived from field VCPUs/params of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} string 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 & object instance \\ \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\_VCPUs\_params}
+
+{\bf Overview:}
+set accessor message derived from field VCPUs/params of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_VCPUs_params (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 & object instance \\ \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\_VCPUs\_number}
+
+{\bf Overview:}
+get accessor message derived from field VCPUs/number of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} int get_VCPUs_number (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 & object instance \\ \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 accessor message derived from field VCPUs/utilisation of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} ((int -> float) Map) get_VCPUs_utilisation (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 & object instance \\ \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\_features\_required}
+
+{\bf Overview:}
+get accessor message derived from field VCPUs/features/required of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} ((cpu_feature) Set) get_VCPUs_features_required (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 & object instance \\ \hline
+
+\end{tabular}
+
+\vspace{0.3cm}
+
+ \noindent {\bf Return Type:}
+{\tt
+(cpu\_feature) Set
+}
+
+
+value of the field
+\vspace{0.3cm}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\subsubsection{RPC name:~get\_VCPUs\_features\_can\_use}
+
+{\bf Overview:}
+get accessor message derived from field VCPUs/features/can\_use of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} ((cpu_feature) Set) get_VCPUs_features_can_use (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 & object instance \\ \hline
+
+\end{tabular}
+
+\vspace{0.3cm}
+
+ \noindent {\bf Return Type:}
+{\tt
+(cpu\_feature) Set
+}
+
+
+value of the field
+\vspace{0.3cm}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\subsubsection{RPC name:~get\_VCPUs\_features\_force\_on}
+
+{\bf Overview:}
+get accessor message derived from field VCPUs/features/force\_on of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} ((cpu_feature) Set) get_VCPUs_features_force_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 & object instance \\ \hline
+
+\end{tabular}
+
+\vspace{0.3cm}
+
+ \noindent {\bf Return Type:}
+{\tt
+(cpu\_feature) Set
+}
+
+
+value of the field
+\vspace{0.3cm}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\subsubsection{RPC name:~add\_VCPUs\_features\_force\_on}
+
+{\bf Overview:}
+set add message derived from field VCPUs/features/force\_on of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void add_VCPUs_features_force_on (session_id s, VM ref self, cpu_feature 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 & object instance \\ \hline
+
+{\tt cpu\_feature } & value & New 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\_VCPUs\_features\_force\_on}
+
+{\bf Overview:}
+set remove message derived from field VCPUs/features/force\_on of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void remove_VCPUs_features_force_on (session_id s, VM ref self, cpu_feature 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 & object instance \\ \hline
+
+{\tt cpu\_feature } & value & Value 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\_features\_force\_off}
+
+{\bf Overview:}
+get accessor message derived from field VCPUs/features/force\_off of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} ((cpu_feature) Set) get_VCPUs_features_force_off (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 & object instance \\ \hline
+
+\end{tabular}
+
+\vspace{0.3cm}
+
+ \noindent {\bf Return Type:}
+{\tt
+(cpu\_feature) Set
+}
+
+
+value of the field
+\vspace{0.3cm}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\subsubsection{RPC name:~add\_VCPUs\_features\_force\_off}
+
+{\bf Overview:}
+set add message derived from field VCPUs/features/force\_off of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void add_VCPUs_features_force_off (session_id s, VM ref self, cpu_feature 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 & object instance \\ \hline
+
+{\tt cpu\_feature } & value & New 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\_VCPUs\_features\_force\_off}
+
+{\bf Overview:}
+set remove message derived from field VCPUs/features/force\_off of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void remove_VCPUs_features_force_off (session_id s, VM ref self, cpu_feature 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 & object instance \\ \hline
+
+{\tt cpu\_feature } & value & Value 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\_actions\_after\_shutdown}
+
+{\bf Overview:}
+get accessor message derived from field actions/after\_shutdown of object 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 & object instance \\ \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 accessor message derived from field actions/after\_shutdown of object 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 & object instance \\ \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 accessor message derived from field actions/after\_reboot of object 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 & object instance \\ \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 accessor message derived from field actions/after\_reboot of object 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 & object instance \\ \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\_suspend}
+
+{\bf Overview:}
+get accessor message derived from field actions/after\_suspend of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} (on_normal_exit) get_actions_after_suspend (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 & object instance \\ \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\_suspend}
+
+{\bf Overview:}
+set accessor message derived from field actions/after\_suspend of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_actions_after_suspend (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 & object instance \\ \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 accessor message derived from field actions/after\_crash of object 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 & object instance \\ \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 accessor message derived from field actions/after\_crash of object 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 & object instance \\ \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\_VIFs}
+
+{\bf Overview:}
+get accessor message derived from field VIFs of object 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 & object instance \\ \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 accessor message derived from field VBDs of object 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 & object instance \\ \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\_VTPMs}
+
+{\bf Overview:}
+get accessor message derived from field VTPMs of object 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 & object instance \\ \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\_bios\_boot}
+
+{\bf Overview:}
+get accessor message derived from field bios/boot of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} string get_bios_boot (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 & object instance \\ \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\_bios\_boot}
+
+{\bf Overview:}
+set accessor message derived from field bios/boot of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_bios_boot (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 & object instance \\ \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\_platform\_std\_VGA}
+
+{\bf Overview:}
+get accessor message derived from field platform/std\_VGA of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} bool get_platform_std_VGA (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 & object instance \\ \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\_platform\_std\_VGA}
+
+{\bf Overview:}
+set accessor message derived from field platform/std\_VGA of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_platform_std_VGA (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 & object instance \\ \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\_platform\_serial}
+
+{\bf Overview:}
+get accessor message derived from field platform/serial of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} string get_platform_serial (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 & object instance \\ \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\_platform\_serial}
+
+{\bf Overview:}
+set accessor message derived from field platform/serial of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_platform_serial (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 & object instance \\ \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\_platform\_localtime}
+
+{\bf Overview:}
+get accessor message derived from field platform/localtime of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} bool get_platform_localtime (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 & object instance \\ \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\_platform\_localtime}
+
+{\bf Overview:}
+set accessor message derived from field platform/localtime of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_platform_localtime (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 & object instance \\ \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\_platform\_clock\_offset}
+
+{\bf Overview:}
+get accessor message derived from field platform/clock\_offset of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} bool get_platform_clock_offset (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 & object instance \\ \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\_platform\_clock\_offset}
+
+{\bf Overview:}
+set accessor message derived from field platform/clock\_offset of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_platform_clock_offset (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 & object instance \\ \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\_platform\_enable\_audio}
+
+{\bf Overview:}
+get accessor message derived from field platform/enable\_audio of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} bool get_platform_enable_audio (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 & object instance \\ \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\_platform\_enable\_audio}
+
+{\bf Overview:}
+set accessor message derived from field platform/enable\_audio of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_platform_enable_audio (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 & object instance \\ \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\_builder}
+
+{\bf Overview:}
+get accessor message derived from field builder of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} string get_builder (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 & object instance \\ \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\_builder}
+
+{\bf Overview:}
+set accessor message derived from field builder of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_builder (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 & object instance \\ \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\_boot\_method}
+
+{\bf Overview:}
+get accessor message derived from field boot\_method of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} (boot_type) get_boot_method (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 & object instance \\ \hline
+
+\end{tabular}
+
+\vspace{0.3cm}
+
+ \noindent {\bf Return Type:}
+{\tt
+boot\_type
+}
+
+
+value of the field
+\vspace{0.3cm}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\subsubsection{RPC name:~set\_boot\_method}
+
+{\bf Overview:}
+set accessor message derived from field boot\_method of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_boot_method (session_id s, VM ref self, boot_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 VM ref } & self & object instance \\ \hline
+
+{\tt boot\_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\_kernel\_kernel}
+
+{\bf Overview:}
+get accessor message derived from field kernel/kernel of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} string get_kernel_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 & object instance \\ \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\_kernel\_kernel}
+
+{\bf Overview:}
+set accessor message derived from field kernel/kernel of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_kernel_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 & object instance \\ \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\_kernel\_initrd}
+
+{\bf Overview:}
+get accessor message derived from field kernel/initrd of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} string get_kernel_initrd (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 & object instance \\ \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\_kernel\_initrd}
+
+{\bf Overview:}
+set accessor message derived from field kernel/initrd of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_kernel_initrd (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 & object instance \\ \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\_kernel\_args}
+
+{\bf Overview:}
+get accessor message derived from field kernel/args of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} string get_kernel_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 & object instance \\ \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\_kernel\_args}
+
+{\bf Overview:}
+set accessor message derived from field kernel/args of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_kernel_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 & object instance \\ \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\_grub\_cmdline}
+
+{\bf Overview:}
+get accessor message derived from field grub/cmdline of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} string get_grub_cmdline (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 & object instance \\ \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\_grub\_cmdline}
+
+{\bf Overview:}
+set accessor message derived from field grub/cmdline of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_grub_cmdline (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 & object instance \\ \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\_PCI\_bus}
+
+{\bf Overview:}
+get accessor message derived from field PCI\_bus of object 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 & object instance \\ \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\_tools\_version}
+
+{\bf Overview:}
+get accessor message derived from field tools\_version of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} ((string -> string) Map) get_tools_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 & object instance \\ \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\_otherConfig}
+
+{\bf Overview:}
+get accessor message derived from field otherConfig of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} ((string -> string) Map) get_otherConfig (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 & object instance \\ \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:~add\_to\_otherConfig}
+
+{\bf Overview:}
+map add message derived from field otherConfig of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void add_to_otherConfig (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 & object instance \\ \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\_otherConfig}
+
+{\bf Overview:}
+map remove message derived from field otherConfig of object VM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void remove_from_otherConfig (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 & object instance \\ \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:}
+constructor for class VM
+
+ \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:}
+destructor for class VM
+
+ \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 & object instance \\ \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:}
+returns the VM instance with a particular 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:}
+returns a record containing the state of an instance of class 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:}
+returns the VM instance with a particular name 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: 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 software\_version} & (string $\rightarrow$ string) Map & version strings \\
+$\mathit{RO}_\mathit{run}$ & {\tt resident\_VMs} & (VM ref) Set & list of VMs currently resident on host \\
+$\mathit{RO}_\mathit{run}$ & {\tt PIFs} & (PIF ref) Set & physical network interfaces \\
+$\mathit{RO}_\mathit{run}$ & {\tt host\_CPUs} & (host\_cpu ref) Set & The physical CPUs on this host \\
+\hline
+\end{longtable}
+\subsection{Additional 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:~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 accessor message derived from field uuid of object 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 & object instance \\ \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 accessor message derived from field name/label of object 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 & object instance \\ \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 accessor message derived from field name/label of object 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 & object instance \\ \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 accessor message derived from field name/description of object 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 & object instance \\ \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 accessor message derived from field name/description of object 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 & object instance \\ \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\_software\_version}
+
+{\bf Overview:}
+get accessor message derived from field software\_version of object 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 & object instance \\ \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\_resident\_VMs}
+
+{\bf Overview:}
+get accessor message derived from field resident\_VMs of object 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 & object instance \\ \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\_PIFs}
+
+{\bf Overview:}
+get accessor message derived from field PIFs of object 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 & object instance \\ \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\_host\_CPUs}
+
+{\bf Overview:}
+get accessor message derived from field host\_CPUs of object 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 & object instance \\ \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:~create}
+
+{\bf Overview:}
+constructor for class host
+
+ \noindent {\bf Signature:}
+\begin{verbatim} (host ref) create (session_id s, host 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 host record } & args & All constructor arguments \\ \hline
+
+\end{tabular}
+
+\vspace{0.3cm}
+
+ \noindent {\bf Return Type:}
+{\tt
+host ref
+}
+
+
+reference to the newly created object
+\vspace{0.3cm}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\subsubsection{RPC name:~destroy}
+
+{\bf Overview:}
+destructor for class host
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void destroy (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 & object instance \\ \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:}
+returns the host instance with a particular 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:}
+returns a record containing the state of an instance of class 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:}
+returns the host instance with a particular name 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\_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{ins}$ & {\tt host} & host ref & the host the CPU is in \\
+$\mathit{RO}_\mathit{ins}$ & {\tt number} & int & the number of the physical CPU within the host \\
+$\mathit{RO}_\mathit{ins}$ & {\tt vendor} & string & the vendor of the physical CPU \\
+$\mathit{RO}_\mathit{ins}$ & {\tt speed} & int & the speed of the physical CPU \\
+$\mathit{RO}_\mathit{ins}$ & {\tt modelname} & string & the model name of the physical CPU \\
+$\mathit{RO}_\mathit{ins}$ & {\tt features} & (cpu\_feature) Set & the features supported by the CPU \\
+$\mathit{RO}_\mathit{run}$ & {\tt utilisation} & float & the current CPU utilisation \\
+\hline
+\end{longtable}
+\subsection{Additional RPCs associated with class: host\_cpu}
+\subsubsection{RPC name:~get\_uuid}
+
+{\bf Overview:}
+get accessor message derived from field uuid of object 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 & object instance \\ \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 accessor message derived from field host of object 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 & object instance \\ \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 accessor message derived from field number of object 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 & object instance \\ \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 accessor message derived from field vendor of object 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 & object instance \\ \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 accessor message derived from field speed of object 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 & object instance \\ \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 accessor message derived from field modelname of object 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 & object instance \\ \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 accessor message derived from field features of object host\_cpu
+
+ \noindent {\bf Signature:}
+\begin{verbatim} ((cpu_feature) Set) 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 & object instance \\ \hline
+
+\end{tabular}
+
+\vspace{0.3cm}
+
+ \noindent {\bf Return Type:}
+{\tt
+(cpu\_feature) Set
+}
+
+
+value of the field
+\vspace{0.3cm}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\subsubsection{RPC name:~get\_utilisation}
+
+{\bf Overview:}
+get accessor message derived from field utilisation of object 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 & object instance \\ \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:~create}
+
+{\bf Overview:}
+constructor for class host\_cpu
+
+ \noindent {\bf Signature:}
+\begin{verbatim} (host_cpu ref) create (session_id s, host_cpu 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 host\_cpu record } & args & All constructor arguments \\ \hline
+
+\end{tabular}
+
+\vspace{0.3cm}
+
+ \noindent {\bf Return Type:}
+{\tt
+host\_cpu ref
+}
+
+
+reference to the newly created object
+\vspace{0.3cm}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\subsubsection{RPC name:~destroy}
+
+{\bf Overview:}
+destructor for class host\_cpu
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void destroy (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 & object instance \\ \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:}
+returns the host\_cpu instance with a particular 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:}
+returns a record containing the state of an instance of class 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 IP address. Used for auto-configuring guests with fixed IP setting \\
+$\mathit{RW}$ & {\tt default\_netmask} & string & default netmask. Used for auto-configuring guests with fixed IP setting \\
+\hline
+\end{longtable}
+\subsection{Additional 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 accessor message derived from field uuid of object 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 & object instance \\ \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 accessor message derived from field name/label of object 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 & object instance \\ \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 accessor message derived from field name/label of object 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 & object instance \\ \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 accessor message derived from field name/description of object 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 & object instance \\ \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 accessor message derived from field name/description of object 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 & object instance \\ \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 accessor message derived from field VIFs of object 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 & object instance \\ \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 accessor message derived from field PIFs of object 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 & object instance \\ \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 accessor message derived from field default\_gateway of object 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 & object instance \\ \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 accessor message derived from field default\_gateway of object 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 & object instance \\ \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 accessor message derived from field default\_netmask of object 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 & object instance \\ \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 accessor message derived from field default\_netmask of object 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 & object instance \\ \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:}
+constructor for class network
+
+ \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:}
+destructor for class network
+
+ \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 & object instance \\ \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:}
+returns the network instance with a particular 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:}
+returns a record containing the state of an instance of class 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:}
+returns the network instance with a particular name 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 name} & string & human-readable name of the interface \\
+$\mathit{RW}$ & {\tt type} & driver\_type & interface type \\
+$\mathit{RW}$ & {\tt device} & string & name of network device as exposed to guest e.g. eth0 \\
+$\mathit{RW}$ & {\tt network} & network ref & virtual network to which this vif is connected \\
+$\mathit{RW}$ & {\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 io/read\_kbs} & float & Read bandwidth (KiB/s) \\
+$\mathit{RO}_\mathit{run}$ & {\tt io/write\_kbs} & float & Write bandwidth (KiB/s) \\
+\hline
+\end{longtable}
+\subsection{Additional RPCs associated with class: VIF}
+\subsubsection{RPC name:~get\_uuid}
+
+{\bf Overview:}
+get accessor message derived from field uuid of object 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 & object instance \\ \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}
+
+{\bf Overview:}
+get accessor message derived from field name of object VIF
+
+ \noindent {\bf Signature:}
+\begin{verbatim} string get_name (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 & object instance \\ \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}
+
+{\bf Overview:}
+set accessor message derived from field name of object VIF
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_name (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 & object instance \\ \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\_type}
+
+{\bf Overview:}
+get accessor message derived from field type of object VIF
+
+ \noindent {\bf Signature:}
+\begin{verbatim} (driver_type) get_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 & object instance \\ \hline
+
+\end{tabular}
+
+\vspace{0.3cm}
+
+ \noindent {\bf Return Type:}
+{\tt
+driver\_type
+}
+
+
+value of the field
+\vspace{0.3cm}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\subsubsection{RPC name:~set\_type}
+
+{\bf Overview:}
+set accessor message derived from field type of object VIF
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_type (session_id s, VIF ref self, driver_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 VIF ref } & self & object instance \\ \hline
+
+{\tt driver\_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\_device}
+
+{\bf Overview:}
+get accessor message derived from field device of object 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 & object instance \\ \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 accessor message derived from field device of object 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 & object instance \\ \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 accessor message derived from field network of object 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 & object instance \\ \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:~set\_network}
+
+{\bf Overview:}
+set accessor message derived from field network of object VIF
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_network (session_id s, VIF ref self, network 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 VIF ref } & self & object instance \\ \hline
+
+{\tt network 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\_VM}
+
+{\bf Overview:}
+get accessor message derived from field VM of object 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 & object instance \\ \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:~set\_VM}
+
+{\bf Overview:}
+set accessor message derived from field VM of object VIF
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_VM (session_id s, VIF ref self, VM 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 VIF ref } & self & object instance \\ \hline
+
+{\tt VM 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\_MAC}
+
+{\bf Overview:}
+get accessor message derived from field MAC of object 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 & object instance \\ \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 accessor message derived from field MAC of object 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 & object instance \\ \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 accessor message derived from field MTU of object 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 & object instance \\ \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 accessor message derived from field MTU of object 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 & object instance \\ \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\_io\_read\_kbs}
+
+{\bf Overview:}
+get accessor message derived from field io/read\_kbs of object VIF
+
+ \noindent {\bf Signature:}
+\begin{verbatim} float get_io_read_kbs (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 & object instance \\ \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 accessor message derived from field io/write\_kbs of object VIF
+
+ \noindent {\bf Signature:}
+\begin{verbatim} float get_io_write_kbs (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 & object instance \\ \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:~create}
+
+{\bf Overview:}
+constructor for class VIF
+
+ \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:}
+destructor for class VIF
+
+ \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 & object instance \\ \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:}
+returns the VIF instance with a particular 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:}
+returns a record containing the state of an instance of class 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: 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 name} & string & human-readable name of the interface \\
+$\mathit{RW}$ & {\tt network} & network ref & virtual network to which this pif is connected \\
+$\mathit{RW}$ & {\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} & string & VLAN tag for all traffic passing through this interface \\
+$\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) \\
+\hline
+\end{longtable}
+\subsection{Additional RPCs associated with class: PIF}
+\subsubsection{RPC name:~get\_uuid}
+
+{\bf Overview:}
+get accessor message derived from field uuid of object 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 & object instance \\ \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}
+
+{\bf Overview:}
+get accessor message derived from field name of object PIF
+
+ \noindent {\bf Signature:}
+\begin{verbatim} string get_name (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 & object instance \\ \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}
+
+{\bf Overview:}
+set accessor message derived from field name of object PIF
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_name (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 & object instance \\ \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 accessor message derived from field network of object 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 & object instance \\ \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:~set\_network}
+
+{\bf Overview:}
+set accessor message derived from field network of object PIF
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_network (session_id s, PIF ref self, network 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 PIF ref } & self & object instance \\ \hline
+
+{\tt network 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\_host}
+
+{\bf Overview:}
+get accessor message derived from field host of object 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 & object instance \\ \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:~set\_host}
+
+{\bf Overview:}
+set accessor message derived from field host of object PIF
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_host (session_id s, PIF ref self, host 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 PIF ref } & self & object instance \\ \hline
+
+{\tt host 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\_MAC}
+
+{\bf Overview:}
+get accessor message derived from field MAC of object 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 & object instance \\ \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 accessor message derived from field MAC of object 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 & object instance \\ \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 accessor message derived from field MTU of object 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 & object instance \\ \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 accessor message derived from field MTU of object 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 & object instance \\ \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 accessor message derived from field VLAN of object PIF
+
+ \noindent {\bf Signature:}
+\begin{verbatim} string 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 & object instance \\ \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\_VLAN}
+
+{\bf Overview:}
+set accessor message derived from field VLAN of object PIF
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_VLAN (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 & object instance \\ \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\_io\_read\_kbs}
+
+{\bf Overview:}
+get accessor message derived from field io/read\_kbs of object PIF
+
+ \noindent {\bf Signature:}
+\begin{verbatim} float get_io_read_kbs (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 & object instance \\ \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 accessor message derived from field io/write\_kbs of object PIF
+
+ \noindent {\bf Signature:}
+\begin{verbatim} float get_io_write_kbs (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 & object instance \\ \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:~create}
+
+{\bf Overview:}
+constructor for class PIF
+
+ \noindent {\bf Signature:}
+\begin{verbatim} (PIF ref) create (session_id s, PIF 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 PIF record } & args & All constructor arguments \\ \hline
+
+\end{tabular}
+
+\vspace{0.3cm}
+
+ \noindent {\bf Return Type:}
+{\tt
+PIF ref
+}
+
+
+reference to the newly created object
+\vspace{0.3cm}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\subsubsection{RPC name:~destroy}
+
+{\bf Overview:}
+destructor for class PIF
+
+ \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 & object instance \\ \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:}
+returns the PIF instance with a particular 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:}
+returns a record containing the state of an instance of class 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: 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 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 location} & string & a string that uniquely determines the location of the storage repository; the format of this string depends on the repository's type \\
+\hline
+\end{longtable}
+\subsection{Additional RPCs associated with class: SR}
+\subsubsection{RPC name:~clone}
+
+{\bf Overview:}
+Take an exact copy of the Storage Repository;
+ the cloned storage repository has the same type as its parent
+
+ \noindent {\bf Signature:}
+\begin{verbatim} (SR ref) clone (session_id s, SR ref sr, string loc, string name)\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 } & sr & The Storage Repository to clone \\ \hline
+
+{\tt string } & loc & The location string that defines where the new storage repository will be located \\ \hline
+
+{\tt string } & name & The name of the new storage repository \\ \hline
+
+\end{tabular}
+
+\vspace{0.3cm}
+
+ \noindent {\bf Return Type:}
+{\tt
+SR ref
+}
+
+
+The ID of the newly created Storage Repository.
+\vspace{0.3cm}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\subsubsection{RPC name:~get\_all}
+
+{\bf Overview:}
+Return a list of all the Storage Repositories 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
+}
+
+
+A list of all the IDs of all the Storage Repositories
+\vspace{0.3cm}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\subsubsection{RPC name:~get\_uuid}
+
+{\bf Overview:}
+get accessor message derived from field uuid of object 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 & object instance \\ \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 accessor message derived from field name/label of object 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 & object instance \\ \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 accessor message derived from field name/label of object 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 & object instance \\ \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 accessor message derived from field name/description of object 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 & object instance \\ \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 accessor message derived from field name/description of object 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 & object instance \\ \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 accessor message derived from field VDIs of object 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 & object instance \\ \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\_virtual\_allocation}
+
+{\bf Overview:}
+get accessor message derived from field virtual\_allocation of object 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 & object instance \\ \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 accessor message derived from field physical\_utilisation of object 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 & object instance \\ \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 accessor message derived from field physical\_size of object 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 & object instance \\ \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 accessor message derived from field type of object 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 & object instance \\ \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\_location}
+
+{\bf Overview:}
+get accessor message derived from field location of object SR
+
+ \noindent {\bf Signature:}
+\begin{verbatim} string get_location (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 & object instance \\ \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:}
+constructor for class SR
+
+ \noindent {\bf Signature:}
+\begin{verbatim} (SR ref) create (session_id s, SR 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 SR record } & args & All constructor arguments \\ \hline
+
+\end{tabular}
+
+\vspace{0.3cm}
+
+ \noindent {\bf Return Type:}
+{\tt
+SR ref
+}
+
+
+reference to the newly created object
+\vspace{0.3cm}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\subsubsection{RPC name:~destroy}
+
+{\bf Overview:}
+destructor for class SR
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void destroy (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 & object instance \\ \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:}
+returns the SR instance with a particular 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:}
+returns a record containing the state of an instance of class 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:}
+returns the SR instance with a particular name 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{RW}$ & {\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{RW}$ & {\tt virtual\_size} & int & size of disk as presented to the guest (in multiples of sector\_size field) \\
+$\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 sector\_size} & int & sector size of VDI (in bytes) \\
+$\mathit{RO}_\mathit{ins}$ & {\tt type} & vdi\_type & type of the VDI \\
+$\mathit{RO}_\mathit{ins}$ & {\tt parent} & VDI ref & parent disk (e.g. in the case of copy on write) \\
+$\mathit{RO}_\mathit{run}$ & {\tt children} & (VDI ref) Set & child disks (e.g. in the case of copy on write) \\
+$\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 \\
+\hline
+\end{longtable}
+\subsection{Additional RPCs associated with class: VDI}
+\subsubsection{RPC name:~snapshot}
+
+{\bf Overview:}
+Take an exact copy of the VDI; the snapshot lives in the same Storage Repository as its parent.
+
+ \noindent {\bf Signature:}
+\begin{verbatim} (VDI ref) snapshot (session_id s, 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 VDI ref } & vdi & The VDI to snapshot \\ \hline
+
+\end{tabular}
+
+\vspace{0.3cm}
+
+ \noindent {\bf Return Type:}
+{\tt
+VDI ref
+}
+
+
+The ID of the newly created VDI.
+\vspace{0.3cm}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\subsubsection{RPC name:~resize}
+
+{\bf Overview:}
+Resize the vdi to the size.
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void resize (session_id s, VDI ref vdi, int size)\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 } & vdi & The VDI to resize \\ \hline
+
+{\tt int } & size & The new size of the VDI \\ \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\_uuid}
+
+{\bf Overview:}
+get accessor message derived from field uuid of object 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 & object instance \\ \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 accessor message derived from field name/label of object 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 & object instance \\ \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 accessor message derived from field name/label of object 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 & object instance \\ \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 accessor message derived from field name/description of object 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 & object instance \\ \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 accessor message derived from field name/description of object 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 & object instance \\ \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 accessor message derived from field SR of object 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 & object instance \\ \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\_SR}
+
+{\bf Overview:}
+set accessor message derived from field SR of object VDI
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_SR (session_id s, VDI 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 VDI ref } & self & object instance \\ \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\_VBDs}
+
+{\bf Overview:}
+get accessor message derived from field VBDs of object 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 & object instance \\ \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\_virtual\_size}
+
+{\bf Overview:}
+get accessor message derived from field virtual\_size of object 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 & object instance \\ \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 accessor message derived from field virtual\_size of object 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 & object instance \\ \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 accessor message derived from field physical\_utilisation of object 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 & object instance \\ \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\_sector\_size}
+
+{\bf Overview:}
+get accessor message derived from field sector\_size of object VDI
+
+ \noindent {\bf Signature:}
+\begin{verbatim} int get_sector_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 & object instance \\ \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 accessor message derived from field type of object 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 & object instance \\ \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\_parent}
+
+{\bf Overview:}
+get accessor message derived from field parent of object VDI
+
+ \noindent {\bf Signature:}
+\begin{verbatim} (VDI ref) get_parent (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 & object instance \\ \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\_children}
+
+{\bf Overview:}
+get accessor message derived from field children of object VDI
+
+ \noindent {\bf Signature:}
+\begin{verbatim} ((VDI ref) Set) get_children (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 & object instance \\ \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\_sharable}
+
+{\bf Overview:}
+get accessor message derived from field sharable of object 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 & object instance \\ \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 accessor message derived from field sharable of object 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 & object instance \\ \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 accessor message derived from field read\_only of object 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 & object instance \\ \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 accessor message derived from field read\_only of object 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 & object instance \\ \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:~create}
+
+{\bf Overview:}
+constructor for class VDI
+
+ \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:}
+destructor for class VDI
+
+ \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 & object instance \\ \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:}
+returns the VDI instance with a particular 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:}
+returns a record containing the state of an instance of class 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:}
+returns the VDI instance with a particular name 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{RW}$ & {\tt VM} & VM ref & the virtual machine \\
+$\mathit{RW}$ & {\tt VDI} & VDI ref & the virtual disk \\
+$\mathit{RW}$ & {\tt device} & string & device seen by the guest e.g. hda1 \\
+$\mathit{RW}$ & {\tt mode} & vbd\_mode & the mode the disk should be mounted with \\
+$\mathit{RW}$ & {\tt driver} & driver\_type & the style of driver \\
+$\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) \\
+\hline
+\end{longtable}
+\subsection{Additional 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:~get\_uuid}
+
+{\bf Overview:}
+get accessor message derived from field uuid of object 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 & object instance \\ \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 accessor message derived from field VM of object 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 & object instance \\ \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:~set\_VM}
+
+{\bf Overview:}
+set accessor message derived from field VM of object VBD
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_VM (session_id s, VBD ref self, VM 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 VBD ref } & self & object instance \\ \hline
+
+{\tt VM 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\_VDI}
+
+{\bf Overview:}
+get accessor message derived from field VDI of object 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 & object instance \\ \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:~set\_VDI}
+
+{\bf Overview:}
+set accessor message derived from field VDI of object VBD
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_VDI (session_id s, VBD ref self, VDI 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 VBD ref } & self & object instance \\ \hline
+
+{\tt VDI 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\_device}
+
+{\bf Overview:}
+get accessor message derived from field device of object 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 & object instance \\ \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 accessor message derived from field device of object 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 & object instance \\ \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\_mode}
+
+{\bf Overview:}
+get accessor message derived from field mode of object 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 & object instance \\ \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 accessor message derived from field mode of object 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 & object instance \\ \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\_driver}
+
+{\bf Overview:}
+get accessor message derived from field driver of object VBD
+
+ \noindent {\bf Signature:}
+\begin{verbatim} (driver_type) get_driver (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 & object instance \\ \hline
+
+\end{tabular}
+
+\vspace{0.3cm}
+
+ \noindent {\bf Return Type:}
+{\tt
+driver\_type
+}
+
+
+value of the field
+\vspace{0.3cm}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\subsubsection{RPC name:~set\_driver}
+
+{\bf Overview:}
+set accessor message derived from field driver of object VBD
+
+ \noindent {\bf Signature:}
+\begin{verbatim} void set_driver (session_id s, VBD ref self, driver_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 & object instance \\ \hline
+
+{\tt driver\_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\_io\_read\_kbs}
+
+{\bf Overview:}
+get accessor message derived from field io/read\_kbs of object VBD
+
+ \noindent {\bf Signature:}
+\begin{verbatim} float get_io_read_kbs (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 & object instance \\ \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 accessor message derived from field io/write\_kbs of object VBD
+
+ \noindent {\bf Signature:}
+\begin{verbatim} float get_io_write_kbs (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 & object instance \\ \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:~create}
+
+{\bf Overview:}
+constructor for class VBD
+
+ \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:}
+destructor for class VBD
+
+ \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 & object instance \\ \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:}
+returns the VBD instance with a particular 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:}
+returns a record containing the state of an instance of class 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: 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{RO}_\mathit{ins}$ & {\tt driver} & driver\_type & the style of driver \\
+$\mathit{RO}_\mathit{ins}$ & {\tt instance} & int & the instance number the virtual TPM represents \\
+\hline
+\end{longtable}
+\subsection{Additional RPCs associated with class: VTPM}
+\subsubsection{RPC name:~get\_uuid}
+
+{\bf Overview:}
+get accessor message derived from field uuid of object 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 & object instance \\ \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 accessor message derived from field VM of object 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 & object instance \\ \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 accessor message derived from field backend of object 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 & object instance \\ \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\_driver}
+
+{\bf Overview:}
+get accessor message derived from field driver of object VTPM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} (driver_type) get_driver (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 & object instance \\ \hline
+
+\end{tabular}
+
+\vspace{0.3cm}
+
+ \noindent {\bf Return Type:}
+{\tt
+driver\_type
+}
+
+
+value of the field
+\vspace{0.3cm}
+\vspace{0.3cm}
+\vspace{0.3cm}
+\subsubsection{RPC name:~get\_instance}
+
+{\bf Overview:}
+get accessor message derived from field instance of object VTPM
+
+ \noindent {\bf Signature:}
+\begin{verbatim} int get_instance (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 & object instance \\ \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:~create}
+
+{\bf Overview:}
+constructor for class VTPM
+
+ \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:}
+destructor for class VTPM
+
+ \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 & object instance \\ \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:}
+returns the VTPM instance with a particular 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:}
+returns a record containing the state of an instance of class 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: 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{Additional RPCs associated with class: user}
+\subsubsection{RPC name:~get\_uuid}
+
+{\bf Overview:}
+get accessor message derived from field uuid of object 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 & object instance \\ \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 accessor message derived from field short\_name of object 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 & object instance \\ \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 accessor message derived from field fullname of object 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 & object instance \\ \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 accessor message derived from field fullname of object 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 & object instance \\ \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:}
+constructor for class user
+
+ \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:}
+destructor for class user
+
+ \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 & object instance \\ \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:}
+returns the user instance with a particular 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:}
+returns a record containing the state of an instance of class 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: debug}
+\subsection{Fields for class: debug}
+{\bf Class debug has no fields.}
+\subsection{Additional 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:}
+constructor for class debug
+
+ \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:}
+destructor for class debug
+
+ \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 & object instance \\ \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:}
+returns the debug instance with a particular 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:}
+returns a record containing the state of an instance of class 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}
+
+\vspace{1cm}
+\section{DTD}
+General notes:
+\begin{itemize}
+\item Values of primitive types (int, bool, etc) and higher-order types (Sets, Maps) are encoded as simple strings, rather than being expanded into XML fragments. For example ``5'', ``true'', ``1, 2, 3, 4'', ``(1, 2), (2, 3), (3, 4)''
+\item Values of enumeration types are represented as strings (e.g. ``PAE'', ``3DNow!'')
+\item Object References are represented as UUIDs, written in string form
+\end{itemize}
diff --git a/docs/xen-api/xenapi.tex b/docs/xen-api/xenapi.tex
new file mode 100644
index 0000000000..ff8cfa9fcc
--- /dev/null
+++ b/docs/xen-api/xenapi.tex
@@ -0,0 +1,56 @@
+%
+% 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.
+%
+
+\documentclass{report}
+
+\usepackage{a4wide}
+\usepackage{graphics}
+\usepackage{longtable}
+
+\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}
+
+% ... and off we go!
+
+\chapter{Introduction}
+
+This document contains a proposal for a Xen Management API---an interface for
+remotely configuring and controlling virtualised guests running on a
+Xen-enabled host.
+
+~
+
+{\bf \large This document is an early draft for discussion purposes only.}
+
+~
+
+\input{presentation}
+
+\include{wire-protocol}
+\include{vm-lifecycle}
+\include{todo}
+\include{xenapi-datamodel}
+\include{fdl}
+
+\end{document}
diff --git a/linux-2.6-xen-sparse/arch/i386/kernel/sysenter.c b/linux-2.6-xen-sparse/arch/i386/kernel/sysenter.c
index 844c87e78c..f300bd159e 100644
--- a/linux-2.6-xen-sparse/arch/i386/kernel/sysenter.c
+++ b/linux-2.6-xen-sparse/arch/i386/kernel/sysenter.c
@@ -60,7 +60,7 @@ int __init sysenter_setup(void)
#ifdef CONFIG_XEN
if (boot_cpu_has(X86_FEATURE_SEP)) {
- struct callback_register sysenter = {
+ static struct callback_register __initdata sysenter = {
.type = CALLBACKTYPE_sysenter,
.address = { __KERNEL_CS, (unsigned long)sysenter_entry },
};
diff --git a/linux-2.6-xen-sparse/arch/i386/mm/hypervisor.c b/linux-2.6-xen-sparse/arch/i386/mm/hypervisor.c
index 5dc6646cf5..7263ef4dea 100644
--- a/linux-2.6-xen-sparse/arch/i386/mm/hypervisor.c
+++ b/linux-2.6-xen-sparse/arch/i386/mm/hypervisor.c
@@ -325,6 +325,7 @@ int xen_create_contiguous_region(
success = (exchange.nr_exchanged == (1UL << order));
BUG_ON(!success && ((exchange.nr_exchanged != 0) || (rc == 0)));
BUG_ON(success && (rc != 0));
+#ifdef CONFIG_XEN_COMPAT_030002
if (unlikely(rc == -ENOSYS)) {
/* Compatibility when XENMEM_exchange is unsupported. */
if (HYPERVISOR_memory_op(XENMEM_decrease_reservation,
@@ -341,6 +342,7 @@ int xen_create_contiguous_region(
BUG();
}
}
+#endif
/* 3. Map the new extent in place of old pages. */
for (i = 0; i < (1UL<<order); i++) {
@@ -419,6 +421,7 @@ void xen_destroy_contiguous_region(unsigned long vstart, unsigned int order)
success = (exchange.nr_exchanged == 1);
BUG_ON(!success && ((exchange.nr_exchanged != 0) || (rc == 0)));
BUG_ON(success && (rc != 0));
+#ifdef CONFIG_XEN_COMPAT_030002
if (unlikely(rc == -ENOSYS)) {
/* Compatibility when XENMEM_exchange is unsupported. */
if (HYPERVISOR_memory_op(XENMEM_decrease_reservation,
@@ -429,6 +432,7 @@ void xen_destroy_contiguous_region(unsigned long vstart, unsigned int order)
BUG();
success = 1;
}
+#endif
/* 4. Map new pages in place of old pages. */
for (i = 0; i < (1UL<<order); i++) {
diff --git a/linux-2.6-xen-sparse/arch/i386/mm/init-xen.c b/linux-2.6-xen-sparse/arch/i386/mm/init-xen.c
index c0c0bb2fa8..4f0175462a 100644
--- a/linux-2.6-xen-sparse/arch/i386/mm/init-xen.c
+++ b/linux-2.6-xen-sparse/arch/i386/mm/init-xen.c
@@ -663,8 +663,8 @@ void __init mem_init(void)
totalram_pages += free_all_bootmem();
/* XEN: init and count low-mem pages outside initial allocation. */
for (pfn = xen_start_info->nr_pages; pfn < max_low_pfn; pfn++) {
- ClearPageReserved(&mem_map[pfn]);
- set_page_count(&mem_map[pfn], 1);
+ ClearPageReserved(pfn_to_page(pfn));
+ set_page_count(pfn_to_page(pfn), 1);
totalram_pages++;
}
diff --git a/linux-2.6-xen-sparse/arch/ia64/xen/xencomm.c b/linux-2.6-xen-sparse/arch/ia64/xen/xencomm.c
index 3767e893fc..367b6b32de 100644
--- a/linux-2.6-xen-sparse/arch/ia64/xen/xencomm.c
+++ b/linux-2.6-xen-sparse/arch/ia64/xen/xencomm.c
@@ -20,6 +20,11 @@
#include <linux/mm.h>
#include <xen/interface/xen.h>
#include <asm/page.h>
+
+#ifdef HAVE_XEN_PLATFORM_COMPAT_H
+#include <xen/platform-compat.h>
+#endif
+
#include <asm/xen/xencomm.h>
static int xencomm_debug = 0;
diff --git a/linux-2.6-xen-sparse/arch/x86_64/mm/init-xen.c b/linux-2.6-xen-sparse/arch/x86_64/mm/init-xen.c
index d3ce58355f..61c75c08a6 100644
--- a/linux-2.6-xen-sparse/arch/x86_64/mm/init-xen.c
+++ b/linux-2.6-xen-sparse/arch/x86_64/mm/init-xen.c
@@ -913,8 +913,8 @@ void __init mem_init(void)
#endif
/* XEN: init and count pages outside initial allocation. */
for (pfn = xen_start_info->nr_pages; pfn < max_pfn; pfn++) {
- ClearPageReserved(&mem_map[pfn]);
- set_page_count(&mem_map[pfn], 1);
+ ClearPageReserved(pfn_to_page(pfn));
+ set_page_count(pfn_to_page(pfn), 1);
totalram_pages++;
}
reservedpages = end_pfn - totalram_pages - e820_hole_size(0, end_pfn);
diff --git a/linux-2.6-xen-sparse/drivers/char/tpm/tpm_xen.c b/linux-2.6-xen-sparse/drivers/char/tpm/tpm_xen.c
index 75a5a6d03e..adf016ba90 100644
--- a/linux-2.6-xen-sparse/drivers/char/tpm/tpm_xen.c
+++ b/linux-2.6-xen-sparse/drivers/char/tpm/tpm_xen.c
@@ -41,6 +41,7 @@
#include <xen/evtchn.h>
#include <xen/interface/grant_table.h>
#include <xen/interface/io/tpmif.h>
+#include <xen/gnttab.h>
#include <xen/xenbus.h>
#include "tpm.h"
#include "tpm_vtpm.h"
diff --git a/linux-2.6-xen-sparse/drivers/xen/blkback/blkback.c b/linux-2.6-xen-sparse/drivers/xen/blkback/blkback.c
index e9a7e7d070..e8df9e0346 100644
--- a/linux-2.6-xen-sparse/drivers/xen/blkback/blkback.c
+++ b/linux-2.6-xen-sparse/drivers/xen/blkback/blkback.c
@@ -189,9 +189,9 @@ static void fast_flush_area(pending_req_t *req)
static void print_stats(blkif_t *blkif)
{
- printk(KERN_DEBUG "%s: oo %3d | rd %4d | wr %4d\n",
+ printk(KERN_DEBUG "%s: oo %3d | rd %4d | wr %4d | br %4d\n",
current->comm, blkif->st_oo_req,
- blkif->st_rd_req, blkif->st_wr_req);
+ blkif->st_rd_req, blkif->st_wr_req, blkif->st_br_req);
blkif->st_print = jiffies + msecs_to_jiffies(10 * 1000);
blkif->st_rd_req = 0;
blkif->st_wr_req = 0;
@@ -241,11 +241,17 @@ int blkif_schedule(void *arg)
* COMPLETION CALLBACK -- Called as bh->b_end_io()
*/
-static void __end_block_io_op(pending_req_t *pending_req, int uptodate)
+static void __end_block_io_op(pending_req_t *pending_req, int error)
{
/* An error fails the entire request. */
- if (!uptodate) {
- DPRINTK("Buffer not up-to-date at end of operation\n");
+ if ((pending_req->operation == BLKIF_OP_WRITE_BARRIER) &&
+ (error == -EOPNOTSUPP)) {
+ DPRINTK("blkback: write barrier op failed, not supported\n");
+ blkback_barrier(XBT_NIL, pending_req->blkif->be, 0);
+ pending_req->status = BLKIF_RSP_EOPNOTSUPP;
+ } else if (error) {
+ DPRINTK("Buffer not up-to-date at end of operation, "
+ "error=%d\n", error);
pending_req->status = BLKIF_RSP_ERROR;
}
@@ -262,7 +268,7 @@ static int end_block_io_op(struct bio *bio, unsigned int done, int error)
{
if (bio->bi_size != 0)
return 1;
- __end_block_io_op(bio->bi_private, !error);
+ __end_block_io_op(bio->bi_private, error);
bio_put(bio);
return error;
}
@@ -319,6 +325,9 @@ static int do_block_io_op(blkif_t *blkif)
blkif->st_rd_req++;
dispatch_rw_block_io(blkif, &req, pending_req);
break;
+ case BLKIF_OP_WRITE_BARRIER:
+ blkif->st_br_req++;
+ /* fall through */
case BLKIF_OP_WRITE:
blkif->st_wr_req++;
dispatch_rw_block_io(blkif, &req, pending_req);
@@ -340,7 +349,6 @@ static void dispatch_rw_block_io(blkif_t *blkif,
pending_req_t *pending_req)
{
extern void ll_rw_block(int rw, int nr, struct buffer_head * bhs[]);
- int operation = (req->operation == BLKIF_OP_WRITE) ? WRITE : READ;
struct gnttab_map_grant_ref map[BLKIF_MAX_SEGMENTS_PER_REQUEST];
struct phys_req preq;
struct {
@@ -349,6 +357,22 @@ static void dispatch_rw_block_io(blkif_t *blkif,
unsigned int nseg;
struct bio *bio = NULL, *biolist[BLKIF_MAX_SEGMENTS_PER_REQUEST];
int ret, i, nbio = 0;
+ int operation;
+
+ switch (req->operation) {
+ case BLKIF_OP_READ:
+ operation = READ;
+ break;
+ case BLKIF_OP_WRITE:
+ operation = WRITE;
+ break;
+ case BLKIF_OP_WRITE_BARRIER:
+ operation = WRITE_BARRIER;
+ break;
+ default:
+ operation = 0; /* make gcc happy */
+ BUG();
+ }
/* Check that number of segments is sane. */
nseg = req->nr_segments;
@@ -364,7 +388,7 @@ static void dispatch_rw_block_io(blkif_t *blkif,
pending_req->blkif = blkif;
pending_req->id = req->id;
- pending_req->operation = operation;
+ pending_req->operation = req->operation;
pending_req->status = BLKIF_RSP_OKAY;
pending_req->nr_pages = nseg;
@@ -380,7 +404,7 @@ static void dispatch_rw_block_io(blkif_t *blkif,
preq.nr_sects += seg[i].nsec;
flags = GNTMAP_host_map;
- if ( operation == WRITE )
+ if (operation != READ)
flags |= GNTMAP_readonly;
gnttab_set_map_op(&map[i], vaddr(pending_req, i), flags,
req->seg[i].gref, blkif->domid);
diff --git a/linux-2.6-xen-sparse/drivers/xen/blkback/common.h b/linux-2.6-xen-sparse/drivers/xen/blkback/common.h
index 38cb756964..d55e388107 100644
--- a/linux-2.6-xen-sparse/drivers/xen/blkback/common.h
+++ b/linux-2.6-xen-sparse/drivers/xen/blkback/common.h
@@ -44,6 +44,7 @@
#include <xen/interface/io/ring.h>
#include <xen/gnttab.h>
#include <xen/driver_util.h>
+#include <xen/xenbus.h>
#define DPRINTK(_f, _a...) \
pr_debug("(file=%s, line=%d) " _f, \
@@ -87,6 +88,7 @@ typedef struct blkif_st {
int st_rd_req;
int st_wr_req;
int st_oo_req;
+ int st_br_req;
wait_queue_head_t waiting_to_free;
@@ -131,4 +133,7 @@ void blkif_xenbus_init(void);
irqreturn_t blkif_be_int(int irq, void *dev_id, struct pt_regs *regs);
int blkif_schedule(void *arg);
+int blkback_barrier(struct xenbus_transaction xbt,
+ struct backend_info *be, int state);
+
#endif /* __BLKIF__BACKEND__COMMON_H__ */
diff --git a/linux-2.6-xen-sparse/drivers/xen/blkback/vbd.c b/linux-2.6-xen-sparse/drivers/xen/blkback/vbd.c
index a809b04cd1..0abd23a521 100644
--- a/linux-2.6-xen-sparse/drivers/xen/blkback/vbd.c
+++ b/linux-2.6-xen-sparse/drivers/xen/blkback/vbd.c
@@ -31,7 +31,6 @@
*/
#include "common.h"
-#include <xen/xenbus.h>
#define vbd_sz(_v) ((_v)->bdev->bd_part ? \
(_v)->bdev->bd_part->nr_sects : (_v)->bdev->bd_disk->capacity)
@@ -104,7 +103,7 @@ int vbd_translate(struct phys_req *req, blkif_t *blkif, int operation)
struct vbd *vbd = &blkif->vbd;
int rc = -EACCES;
- if ((operation == WRITE) && vbd->readonly)
+ if ((operation != READ) && vbd->readonly)
goto out;
if (unlikely((req->sector_number + req->nr_sects) > vbd_sz(vbd)))
diff --git a/linux-2.6-xen-sparse/drivers/xen/blkback/xenbus.c b/linux-2.6-xen-sparse/drivers/xen/blkback/xenbus.c
index 02f90a6803..4d23434b71 100644
--- a/linux-2.6-xen-sparse/drivers/xen/blkback/xenbus.c
+++ b/linux-2.6-xen-sparse/drivers/xen/blkback/xenbus.c
@@ -20,7 +20,6 @@
#include <stdarg.h>
#include <linux/module.h>
#include <linux/kthread.h>
-#include <xen/xenbus.h>
#include "common.h"
#undef DPRINTK
@@ -91,11 +90,13 @@ static void update_blkif_status(blkif_t *blkif)
VBD_SHOW(oo_req, "%d\n", be->blkif->st_oo_req);
VBD_SHOW(rd_req, "%d\n", be->blkif->st_rd_req);
VBD_SHOW(wr_req, "%d\n", be->blkif->st_wr_req);
+VBD_SHOW(br_req, "%d\n", be->blkif->st_br_req);
static struct attribute *vbdstat_attrs[] = {
&dev_attr_oo_req.attr,
&dev_attr_rd_req.attr,
&dev_attr_wr_req.attr,
+ &dev_attr_br_req.attr,
NULL
};
@@ -165,6 +166,19 @@ static int blkback_remove(struct xenbus_device *dev)
return 0;
}
+int blkback_barrier(struct xenbus_transaction xbt,
+ struct backend_info *be, int state)
+{
+ struct xenbus_device *dev = be->dev;
+ int err;
+
+ err = xenbus_printf(xbt, dev->nodename, "feature-barrier",
+ "%d", state);
+ if (err)
+ xenbus_dev_fatal(dev, err, "writing feature-barrier");
+
+ return err;
+}
/**
* Entry point to this code when a new device is created. Allocate the basic
@@ -366,12 +380,15 @@ static void connect(struct backend_info *be)
/* Supply the information about the device the frontend needs */
again:
err = xenbus_transaction_start(&xbt);
-
if (err) {
xenbus_dev_fatal(dev, err, "starting transaction");
return;
}
+ err = blkback_barrier(xbt, be, 1);
+ if (err)
+ goto abort;
+
err = xenbus_printf(xbt, dev->nodename, "sectors", "%lu",
vbd_size(&be->blkif->vbd));
if (err) {
diff --git a/linux-2.6-xen-sparse/drivers/xen/blkfront/blkfront.c b/linux-2.6-xen-sparse/drivers/xen/blkfront/blkfront.c
index 63ebf8ed93..557288b45a 100644
--- a/linux-2.6-xen-sparse/drivers/xen/blkfront/blkfront.c
+++ b/linux-2.6-xen-sparse/drivers/xen/blkfront/blkfront.c
@@ -320,6 +320,12 @@ static void connect(struct blkfront_info *info)
return;
}
+ err = xenbus_gather(XBT_NIL, info->xbdev->otherend,
+ "feature-barrier", "%lu", &info->feature_barrier,
+ NULL);
+ if (err)
+ info->feature_barrier = 0;
+
err = xlvbd_add(sectors, info->vdevice, binfo, sector_size, info);
if (err) {
xenbus_dev_fatal(info->xbdev, err, "xlvbd_add at %s",
@@ -569,11 +575,14 @@ static int blkif_queue_request(struct request *req)
info->shadow[id].request = (unsigned long)req;
ring_req->id = id;
- ring_req->operation = rq_data_dir(req) ?
- BLKIF_OP_WRITE : BLKIF_OP_READ;
ring_req->sector_number = (blkif_sector_t)req->sector;
ring_req->handle = info->handle;
+ ring_req->operation = rq_data_dir(req) ?
+ BLKIF_OP_WRITE : BLKIF_OP_READ;
+ if (blk_barrier_rq(req))
+ ring_req->operation = BLKIF_OP_WRITE_BARRIER;
+
ring_req->nr_segments = 0;
rq_for_each_bio (bio, req) {
bio_for_each_segment (bvec, bio, idx) {
@@ -670,6 +679,7 @@ static irqreturn_t blkif_int(int irq, void *dev_id, struct pt_regs *ptregs)
RING_IDX i, rp;
unsigned long flags;
struct blkfront_info *info = (struct blkfront_info *)dev_id;
+ int uptodate;
spin_lock_irqsave(&blkif_io_lock, flags);
@@ -694,19 +704,27 @@ static irqreturn_t blkif_int(int irq, void *dev_id, struct pt_regs *ptregs)
ADD_ID_TO_FREELIST(info, id);
+ uptodate = (bret->status == BLKIF_RSP_OKAY);
switch (bret->operation) {
+ case BLKIF_OP_WRITE_BARRIER:
+ if (unlikely(bret->status == BLKIF_RSP_EOPNOTSUPP)) {
+ printk("blkfront: %s: write barrier op failed\n",
+ info->gd->disk_name);
+ uptodate = -EOPNOTSUPP;
+ info->feature_barrier = 0;
+ xlvbd_barrier(info);
+ }
+ /* fall through */
case BLKIF_OP_READ:
case BLKIF_OP_WRITE:
if (unlikely(bret->status != BLKIF_RSP_OKAY))
DPRINTK("Bad return from blkdev data "
"request: %x\n", bret->status);
- ret = end_that_request_first(
- req, (bret->status == BLKIF_RSP_OKAY),
+ ret = end_that_request_first(req, uptodate,
req->hard_nr_sectors);
BUG_ON(ret);
- end_that_request_last(
- req, (bret->status == BLKIF_RSP_OKAY));
+ end_that_request_last(req, uptodate);
break;
default:
BUG();
diff --git a/linux-2.6-xen-sparse/drivers/xen/blkfront/block.h b/linux-2.6-xen-sparse/drivers/xen/blkfront/block.h
index 5ba3d1ebc3..b86360f405 100644
--- a/linux-2.6-xen-sparse/drivers/xen/blkfront/block.h
+++ b/linux-2.6-xen-sparse/drivers/xen/blkfront/block.h
@@ -126,6 +126,7 @@ struct blkfront_info
struct gnttab_free_callback callback;
struct blk_shadow shadow[BLK_RING_SIZE];
unsigned long shadow_free;
+ int feature_barrier;
/**
* The number of people holding this device open. We won't allow a
@@ -152,5 +153,6 @@ extern void do_blkif_request (request_queue_t *rq);
int xlvbd_add(blkif_sector_t capacity, int device,
u16 vdisk_info, u16 sector_size, struct blkfront_info *info);
void xlvbd_del(struct blkfront_info *info);
+int xlvbd_barrier(struct blkfront_info *info);
#endif /* __XEN_DRIVERS_BLOCK_H__ */
diff --git a/linux-2.6-xen-sparse/drivers/xen/blkfront/vbd.c b/linux-2.6-xen-sparse/drivers/xen/blkfront/vbd.c
index 0c8b508c9a..f040a2b7e3 100644
--- a/linux-2.6-xen-sparse/drivers/xen/blkfront/vbd.c
+++ b/linux-2.6-xen-sparse/drivers/xen/blkfront/vbd.c
@@ -50,7 +50,7 @@
*/
#define NUM_IDE_MAJORS 10
-#define NUM_SCSI_MAJORS 9
+#define NUM_SCSI_MAJORS 17
#define NUM_VBD_MAJORS 1
static struct xlbd_type_info xlbd_ide_type = {
@@ -165,8 +165,11 @@ xlbd_get_major_info(int vdevice)
case SCSI_DISK1_MAJOR ... SCSI_DISK7_MAJOR:
index = 11 + major - SCSI_DISK1_MAJOR;
break;
- case SCSI_CDROM_MAJOR: index = 18; break;
- default: index = 19; break;
+ case SCSI_DISK8_MAJOR ... SCSI_DISK15_MAJOR:
+ index = 18 + major - SCSI_DISK8_MAJOR;
+ break;
+ case SCSI_CDROM_MAJOR: index = 26; break;
+ default: index = 27; break;
}
mi = ((major_info[index] != NULL) ? major_info[index] :
@@ -227,6 +230,7 @@ xlvbd_alloc_gendisk(int minor, blkif_sector_t capacity, int vdevice,
struct xlbd_major_info *mi;
int nr_minors = 1;
int err = -ENODEV;
+ unsigned int offset;
BUG_ON(info->gd != NULL);
BUG_ON(info->mi != NULL);
@@ -244,15 +248,33 @@ xlvbd_alloc_gendisk(int minor, blkif_sector_t capacity, int vdevice,
if (gd == NULL)
goto out;
- if (nr_minors > 1)
- sprintf(gd->disk_name, "%s%c", mi->type->diskname,
- 'a' + mi->index * mi->type->disks_per_major +
- (minor >> mi->type->partn_shift));
- else
- sprintf(gd->disk_name, "%s%c%d", mi->type->diskname,
- 'a' + mi->index * mi->type->disks_per_major +
- (minor >> mi->type->partn_shift),
- minor & ((1 << mi->type->partn_shift) - 1));
+ offset = mi->index * mi->type->disks_per_major +
+ (minor >> mi->type->partn_shift);
+ if (nr_minors > 1) {
+ if (offset < 26) {
+ sprintf(gd->disk_name, "%s%c",
+ mi->type->diskname, 'a' + offset );
+ }
+ else {
+ sprintf(gd->disk_name, "%s%c%c",
+ mi->type->diskname,
+ 'a' + ((offset/26)-1), 'a' + (offset%26) );
+ }
+ }
+ else {
+ if (offset < 26) {
+ sprintf(gd->disk_name, "%s%c%d",
+ mi->type->diskname,
+ 'a' + offset,
+ minor & ((1 << mi->type->partn_shift) - 1));
+ }
+ else {
+ sprintf(gd->disk_name, "%s%c%c%d",
+ mi->type->diskname,
+ 'a' + ((offset/26)-1), 'a' + (offset%26),
+ minor & ((1 << mi->type->partn_shift) - 1));
+ }
+ }
gd->major = mi->major;
gd->first_minor = minor;
@@ -267,6 +289,10 @@ xlvbd_alloc_gendisk(int minor, blkif_sector_t capacity, int vdevice,
}
info->rq = gd->queue;
+ info->gd = gd;
+
+ if (info->feature_barrier)
+ xlvbd_barrier(info);
if (vdisk_info & VDISK_READONLY)
set_disk_ro(gd, 1);
@@ -277,8 +303,6 @@ xlvbd_alloc_gendisk(int minor, blkif_sector_t capacity, int vdevice,
if (vdisk_info & VDISK_CDROM)
gd->flags |= GENHD_FL_CD;
- info->gd = gd;
-
return 0;
out:
@@ -326,3 +350,26 @@ xlvbd_del(struct blkfront_info *info)
blk_cleanup_queue(info->rq);
info->rq = NULL;
}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
+int
+xlvbd_barrier(struct blkfront_info *info)
+{
+ int err;
+
+ err = blk_queue_ordered(info->rq,
+ info->feature_barrier ? QUEUE_ORDERED_DRAIN : QUEUE_ORDERED_NONE, NULL);
+ if (err)
+ return err;
+ printk("blkfront: %s: barriers %s\n",
+ info->gd->disk_name, info->feature_barrier ? "enabled" : "disabled");
+ return 0;
+}
+#else
+int
+xlvbd_barrier(struct blkfront_info *info)
+{
+ printk("blkfront: %s: barriers disabled\n", info->gd->disk_name);
+ return -ENOSYS;
+}
+#endif
diff --git a/linux-2.6-xen-sparse/drivers/xen/blktap/blktap.c b/linux-2.6-xen-sparse/drivers/xen/blktap/blktap.c
index aef8aa0607..22ea35ae7e 100644
--- a/linux-2.6-xen-sparse/drivers/xen/blktap/blktap.c
+++ b/linux-2.6-xen-sparse/drivers/xen/blktap/blktap.c
@@ -93,8 +93,9 @@ int setup_xen_class(void)
* mmap_alloc is initialised to 2 and should be adjustable on the fly via
* sysfs.
*/
-#define MAX_DYNAMIC_MEM 64
-#define MAX_PENDING_REQS 64
+#define BLK_RING_SIZE __RING_SIZE((blkif_sring_t *)0, PAGE_SIZE)
+#define MAX_DYNAMIC_MEM BLK_RING_SIZE
+#define MAX_PENDING_REQS BLK_RING_SIZE
#define MMAP_PAGES (MAX_PENDING_REQS * BLKIF_MAX_SEGMENTS_PER_REQUEST)
#define MMAP_VADDR(_start, _req,_seg) \
(_start + \
@@ -215,6 +216,7 @@ struct grant_handle_pair
grant_handle_t kernel;
grant_handle_t user;
};
+#define INVALID_GRANT_HANDLE 0xFFFF
static struct grant_handle_pair
pending_grant_handles[MAX_DYNAMIC_MEM][MMAP_PAGES];
@@ -293,10 +295,11 @@ static inline int GET_NEXT_REQ(unsigned long *idx_map)
#define BLKTAP_INVALID_HANDLE(_g) \
- (((_g->kernel) == 0xFFFF) && ((_g->user) == 0xFFFF))
+ (((_g->kernel) == INVALID_GRANT_HANDLE) && \
+ ((_g->user) == INVALID_GRANT_HANDLE))
#define BLKTAP_INVALIDATE_HANDLE(_g) do { \
- (_g)->kernel = 0xFFFF; (_g)->user = 0xFFFF; \
+ (_g)->kernel = INVALID_GRANT_HANDLE; (_g)->user = INVALID_GRANT_HANDLE; \
} while(0)
@@ -535,8 +538,10 @@ static int blktap_release(struct inode *inode, struct file *filp)
}
if ( (info->status != CLEANSHUTDOWN) && (info->blkif != NULL) ) {
- kthread_stop(info->blkif->xenblkd);
- info->blkif->xenblkd = NULL;
+ if (info->blkif->xenblkd != NULL) {
+ kthread_stop(info->blkif->xenblkd);
+ info->blkif->xenblkd = NULL;
+ }
info->status = CLEANSHUTDOWN;
}
return 0;
@@ -588,8 +593,6 @@ static int blktap_mmap(struct file *filp, struct vm_area_struct *vma)
info->user_vstart = info->rings_vstart + (RING_PAGES << PAGE_SHIFT);
/* Map the ring pages to the start of the region and reserve it. */
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
-
if (remap_pfn_range(vma, vma->vm_start,
__pa(info->ufe_ring.sring) >> PAGE_SHIFT,
PAGE_SIZE, vma->vm_page_prot)) {
@@ -884,6 +887,15 @@ static void fast_flush_area(pending_req_t *req, int k_idx, int u_idx,
return;
}
+ if (info->vma != NULL &&
+ xen_feature(XENFEAT_auto_translated_physmap)) {
+ down_write(&info->vma->vm_mm->mmap_sem);
+ zap_page_range(info->vma,
+ MMAP_VADDR(info->user_vstart, u_idx, 0),
+ req->nr_pages << PAGE_SHIFT, NULL);
+ up_write(&info->vma->vm_mm->mmap_sem);
+ }
+
mmap_idx = req->mem_idx;
for (i = 0; i < req->nr_pages; i++) {
@@ -892,14 +904,15 @@ static void fast_flush_area(pending_req_t *req, int k_idx, int u_idx,
khandle = &pending_handle(mmap_idx, k_idx, i);
- if (khandle->kernel != 0xFFFF) {
+ if (khandle->kernel != INVALID_GRANT_HANDLE) {
gnttab_set_unmap_op(&unmap[invcount],
idx_to_kaddr(mmap_idx, k_idx, i),
GNTMAP_host_map, khandle->kernel);
invcount++;
}
- if (khandle->user != 0xFFFF) {
+ if (khandle->user != INVALID_GRANT_HANDLE) {
+ BUG_ON(xen_feature(XENFEAT_auto_translated_physmap));
if (create_lookup_pte_addr(
info->vma->vm_mm,
MMAP_VADDR(info->user_vstart, u_idx, i),
@@ -908,8 +921,10 @@ static void fast_flush_area(pending_req_t *req, int k_idx, int u_idx,
return;
}
- gnttab_set_unmap_op(&unmap[invcount],
- ptep, GNTMAP_host_map,
+ gnttab_set_unmap_op(&unmap[invcount], ptep,
+ GNTMAP_host_map
+ | GNTMAP_application_map
+ | GNTMAP_contains_pte,
khandle->user);
invcount++;
}
@@ -920,7 +935,7 @@ static void fast_flush_area(pending_req_t *req, int k_idx, int u_idx,
GNTTABOP_unmap_grant_ref, unmap, invcount);
BUG_ON(ret);
- if (info->vma != NULL)
+ if (info->vma != NULL && !xen_feature(XENFEAT_auto_translated_physmap))
zap_page_range(info->vma,
MMAP_VADDR(info->user_vstart, u_idx, 0),
req->nr_pages << PAGE_SHIFT, NULL);
@@ -1004,11 +1019,14 @@ static int blktap_read_ufe_ring(tap_blkif_t *info)
rmb();
for (i = info->ufe_ring.rsp_cons; i != rp; i++) {
+ blkif_response_t res;
resp = RING_GET_RESPONSE(&info->ufe_ring, i);
+ memcpy(&res, resp, sizeof(res));
+ mb(); /* rsp_cons read by RING_FULL() in do_block_io_op(). */
++info->ufe_ring.rsp_cons;
/*retrieve [usr_idx] to [mmap_idx,pending_idx] mapping*/
- usr_idx = (int)resp->id;
+ usr_idx = (int)res.id;
pending_idx = MASK_PEND_IDX(ID_TO_IDX(info->idx_map[usr_idx]));
mmap_idx = ID_TO_MIDX(info->idx_map[usr_idx]);
@@ -1041,8 +1059,8 @@ static int blktap_read_ufe_ring(tap_blkif_t *info)
map[offset] = NULL;
}
fast_flush_area(pending_req, pending_idx, usr_idx, info->minor);
- make_response(blkif, pending_req->id, resp->operation,
- resp->status);
+ make_response(blkif, pending_req->id, res.operation,
+ res.status);
info->idx_map[usr_idx] = INVALID_REQ;
blkif_put(pending_req->blkif);
free_req(pending_req);
@@ -1184,8 +1202,10 @@ static void dispatch_rw_block_io(blkif_t *blkif,
/* Check we have space on user ring - should never fail. */
usr_idx = GET_NEXT_REQ(info->idx_map);
- if (usr_idx == INVALID_REQ)
+ if (usr_idx == INVALID_REQ) {
+ BUG();
goto fail_response;
+ }
/* Check that number of segments is sane. */
nseg = req->nr_segments;
@@ -1219,14 +1239,12 @@ static void dispatch_rw_block_io(blkif_t *blkif,
unsigned long uvaddr;
unsigned long kvaddr;
uint64_t ptep;
- struct page *page;
uint32_t flags;
uvaddr = MMAP_VADDR(info->user_vstart, usr_idx, i);
kvaddr = idx_to_kaddr(mmap_idx, pending_idx, i);
- page = virt_to_page(kvaddr);
- sector = req->sector_number + (8*i);
+ sector = req->sector_number + ((PAGE_SIZE / 512) * i);
if( (blkif->sectors > 0) && (sector >= blkif->sectors) ) {
WPRINTK("BLKTAP: Sector request greater"
"than size\n");
@@ -1236,7 +1254,7 @@ static void dispatch_rw_block_io(blkif_t *blkif,
BLKIF_OP_WRITE ? "WRITE" : "READ"),
(long long unsigned) sector,
(long long unsigned) sector>>9,
- blkif->sectors);
+ (long long unsigned) blkif->sectors);
}
flags = GNTMAP_host_map;
@@ -1246,68 +1264,103 @@ static void dispatch_rw_block_io(blkif_t *blkif,
req->seg[i].gref, blkif->domid);
op++;
- /* Now map it to user. */
- ret = create_lookup_pte_addr(info->vma->vm_mm,
- uvaddr, &ptep);
- if (ret) {
- WPRINTK("Couldn't get a pte addr!\n");
- goto fail_flush;
- }
+ if (!xen_feature(XENFEAT_auto_translated_physmap)) {
+ /* Now map it to user. */
+ ret = create_lookup_pte_addr(info->vma->vm_mm,
+ uvaddr, &ptep);
+ if (ret) {
+ WPRINTK("Couldn't get a pte addr!\n");
+ goto fail_flush;
+ }
- flags = GNTMAP_host_map | GNTMAP_application_map
- | GNTMAP_contains_pte;
- if (operation == WRITE)
- flags |= GNTMAP_readonly;
- gnttab_set_map_op(&map[op], ptep, flags,
- req->seg[i].gref, blkif->domid);
- op++;
+ flags = GNTMAP_host_map | GNTMAP_application_map
+ | GNTMAP_contains_pte;
+ if (operation == WRITE)
+ flags |= GNTMAP_readonly;
+ gnttab_set_map_op(&map[op], ptep, flags,
+ req->seg[i].gref, blkif->domid);
+ op++;
+ }
}
ret = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, map, op);
BUG_ON(ret);
- for (i = 0; i < (nseg*2); i+=2) {
- unsigned long uvaddr;
- unsigned long kvaddr;
- unsigned long offset;
- struct page *pg;
+ if (!xen_feature(XENFEAT_auto_translated_physmap)) {
+ for (i = 0; i < (nseg*2); i+=2) {
+ unsigned long uvaddr;
+ unsigned long kvaddr;
+ unsigned long offset;
+ struct page *pg;
- uvaddr = MMAP_VADDR(info->user_vstart, usr_idx, i/2);
- kvaddr = idx_to_kaddr(mmap_idx, pending_idx, i/2);
+ uvaddr = MMAP_VADDR(info->user_vstart, usr_idx, i/2);
+ kvaddr = idx_to_kaddr(mmap_idx, pending_idx, i/2);
- if (unlikely(map[i].status != 0)) {
- WPRINTK("invalid kernel buffer -- "
- "could not remap it\n");
- ret |= 1;
- map[i].handle = 0xFFFF;
- }
+ if (unlikely(map[i].status != 0)) {
+ WPRINTK("invalid kernel buffer -- "
+ "could not remap it\n");
+ ret |= 1;
+ map[i].handle = INVALID_GRANT_HANDLE;
+ }
+
+ if (unlikely(map[i+1].status != 0)) {
+ WPRINTK("invalid user buffer -- "
+ "could not remap it\n");
+ ret |= 1;
+ map[i+1].handle = INVALID_GRANT_HANDLE;
+ }
- if (unlikely(map[i+1].status != 0)) {
- WPRINTK("invalid user buffer -- "
- "could not remap it\n");
- ret |= 1;
- map[i+1].handle = 0xFFFF;
+ pending_handle(mmap_idx, pending_idx, i/2).kernel
+ = map[i].handle;
+ pending_handle(mmap_idx, pending_idx, i/2).user
+ = map[i+1].handle;
+
+ if (ret)
+ continue;
+
+ set_phys_to_machine(__pa(kvaddr) >> PAGE_SHIFT,
+ FOREIGN_FRAME(map[i].dev_bus_addr
+ >> PAGE_SHIFT));
+ offset = (uvaddr - info->vma->vm_start) >> PAGE_SHIFT;
+ pg = pfn_to_page(__pa(kvaddr) >> PAGE_SHIFT);
+ ((struct page **)info->vma->vm_private_data)[offset] =
+ pg;
}
+ } else {
+ for (i = 0; i < nseg; i++) {
+ unsigned long uvaddr;
+ unsigned long kvaddr;
+ unsigned long offset;
+ struct page *pg;
- pending_handle(mmap_idx, pending_idx, i/2).kernel
- = map[i].handle;
- pending_handle(mmap_idx, pending_idx, i/2).user
- = map[i+1].handle;
+ uvaddr = MMAP_VADDR(info->user_vstart, usr_idx, i);
+ kvaddr = idx_to_kaddr(mmap_idx, pending_idx, i);
- if (ret)
- continue;
+ if (unlikely(map[i].status != 0)) {
+ WPRINTK("invalid kernel buffer -- "
+ "could not remap it\n");
+ ret |= 1;
+ map[i].handle = INVALID_GRANT_HANDLE;
+ }
- set_phys_to_machine(__pa(kvaddr) >> PAGE_SHIFT,
- FOREIGN_FRAME(map[i].dev_bus_addr >> PAGE_SHIFT));
- offset = (uvaddr - info->vma->vm_start) >> PAGE_SHIFT;
- pg = pfn_to_page(__pa(kvaddr) >> PAGE_SHIFT);
- ((struct page **)info->vma->vm_private_data)[offset] =
- pg;
+ pending_handle(mmap_idx, pending_idx, i).kernel
+ = map[i].handle;
+
+ if (ret)
+ continue;
+
+ offset = (uvaddr - info->vma->vm_start) >> PAGE_SHIFT;
+ pg = pfn_to_page(__pa(kvaddr) >> PAGE_SHIFT);
+ ((struct page **)info->vma->vm_private_data)[offset] =
+ pg;
+ }
}
if (ret)
goto fail_flush;
+ if (xen_feature(XENFEAT_auto_translated_physmap))
+ down_write(&info->vma->vm_mm->mmap_sem);
/* Mark mapped pages as reserved: */
for (i = 0; i < req->nr_segments; i++) {
unsigned long kvaddr;
@@ -1316,7 +1369,18 @@ static void dispatch_rw_block_io(blkif_t *blkif,
kvaddr = idx_to_kaddr(mmap_idx, pending_idx, i);
pg = pfn_to_page(__pa(kvaddr) >> PAGE_SHIFT);
SetPageReserved(pg);
+ if (xen_feature(XENFEAT_auto_translated_physmap)) {
+ ret = vm_insert_page(info->vma,
+ MMAP_VADDR(info->user_vstart,
+ usr_idx, i), pg);
+ if (ret) {
+ up_write(&info->vma->vm_mm->mmap_sem);
+ goto fail_flush;
+ }
+ }
}
+ if (xen_feature(XENFEAT_auto_translated_physmap))
+ up_write(&info->vma->vm_mm->mmap_sem);
/*record [mmap_idx,pending_idx] to [usr_idx] mapping*/
info->idx_map[usr_idx] = MAKE_ID(mmap_idx, pending_idx);
@@ -1327,6 +1391,7 @@ static void dispatch_rw_block_io(blkif_t *blkif,
info->ufe_ring.req_prod_pvt);
memcpy(target, req, sizeof(*req));
target->id = usr_idx;
+ wmb(); /* blktap_poll() reads req_prod_pvt asynchronously */
info->ufe_ring.req_prod_pvt++;
return;
diff --git a/linux-2.6-xen-sparse/drivers/xen/blktap/xenbus.c b/linux-2.6-xen-sparse/drivers/xen/blktap/xenbus.c
index b08e1dcc1d..553ad45c48 100644
--- a/linux-2.6-xen-sparse/drivers/xen/blktap/xenbus.c
+++ b/linux-2.6-xen-sparse/drivers/xen/blktap/xenbus.c
@@ -189,7 +189,7 @@ static int blktap_probe(struct xenbus_device *dev,
return 0;
fail:
- DPRINTK("blktap probe failed");
+ DPRINTK("blktap probe failed\n");
blktap_remove(dev);
return err;
}
@@ -243,7 +243,7 @@ static void tap_frontend_changed(struct xenbus_device *dev,
struct backend_info *be = dev->dev.driver_data;
int err;
- DPRINTK("");
+ DPRINTK("\n");
switch (frontend_state) {
case XenbusStateInitialising:
@@ -318,7 +318,7 @@ static int connect_ring(struct backend_info *be)
unsigned int evtchn;
int err;
- DPRINTK("%s", dev->otherend);
+ DPRINTK("%s\n", dev->otherend);
err = xenbus_gather(XBT_NIL, dev->otherend, "ring-ref", "%lu",
&ring_ref, "event-channel", "%u", &evtchn, NULL);
diff --git a/linux-2.6-xen-sparse/drivers/xen/core/Makefile b/linux-2.6-xen-sparse/drivers/xen/core/Makefile
index c1b0c1bd51..6154454339 100644
--- a/linux-2.6-xen-sparse/drivers/xen/core/Makefile
+++ b/linux-2.6-xen-sparse/drivers/xen/core/Makefile
@@ -9,5 +9,5 @@ obj-$(CONFIG_SYSFS) += hypervisor_sysfs.o
obj-$(CONFIG_HOTPLUG_CPU) += cpu_hotplug.o
obj-$(CONFIG_XEN_SYSFS) += xen_sysfs.o
obj-$(CONFIG_XEN_SKBUFF) += skbuff.o
-obj-$(CONFIG_XEN_REBOOT) += reboot.o
+obj-$(CONFIG_XEN_REBOOT) += reboot.o machine_reboot.o
obj-$(CONFIG_XEN_SMPBOOT) += smpboot.o
diff --git a/linux-2.6-xen-sparse/drivers/xen/core/machine_reboot.c b/linux-2.6-xen-sparse/drivers/xen/core/machine_reboot.c
new file mode 100644
index 0000000000..02ee7f4728
--- /dev/null
+++ b/linux-2.6-xen-sparse/drivers/xen/core/machine_reboot.c
@@ -0,0 +1,185 @@
+#define __KERNEL_SYSCALLS__
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/unistd.h>
+#include <linux/module.h>
+#include <linux/reboot.h>
+#include <linux/sysrq.h>
+#include <linux/stringify.h>
+#include <asm/irq.h>
+#include <asm/mmu_context.h>
+#include <xen/evtchn.h>
+#include <asm/hypervisor.h>
+#include <xen/interface/dom0_ops.h>
+#include <xen/xenbus.h>
+#include <linux/cpu.h>
+#include <linux/kthread.h>
+#include <xen/gnttab.h>
+#include <xen/xencons.h>
+#include <xen/cpu_hotplug.h>
+
+#if defined(__i386__) || defined(__x86_64__)
+
+/*
+ * Power off function, if any
+ */
+void (*pm_power_off)(void);
+EXPORT_SYMBOL(pm_power_off);
+
+void machine_emergency_restart(void)
+{
+ /* We really want to get pending console data out before we die. */
+ xencons_force_flush();
+ HYPERVISOR_shutdown(SHUTDOWN_reboot);
+}
+
+void machine_restart(char * __unused)
+{
+ machine_emergency_restart();
+}
+
+void machine_halt(void)
+{
+ machine_power_off();
+}
+
+void machine_power_off(void)
+{
+ /* We really want to get pending console data out before we die. */
+ xencons_force_flush();
+ if (pm_power_off)
+ pm_power_off();
+ HYPERVISOR_shutdown(SHUTDOWN_poweroff);
+}
+
+int reboot_thru_bios = 0; /* for dmi_scan.c */
+EXPORT_SYMBOL(machine_restart);
+EXPORT_SYMBOL(machine_halt);
+EXPORT_SYMBOL(machine_power_off);
+
+/* Ensure we run on the idle task page tables so that we will
+ switch page tables before running user space. This is needed
+ on architectures with separate kernel and user page tables
+ because the user page table pointer is not saved/restored. */
+static void switch_idle_mm(void)
+{
+ struct mm_struct *mm = current->active_mm;
+
+ if (mm == &init_mm)
+ return;
+
+ atomic_inc(&init_mm.mm_count);
+ switch_mm(mm, &init_mm, current);
+ current->active_mm = &init_mm;
+ mmdrop(mm);
+}
+
+static void pre_suspend(void)
+{
+ HYPERVISOR_shared_info = (shared_info_t *)empty_zero_page;
+ clear_fixmap(FIX_SHARED_INFO);
+
+ xen_start_info->store_mfn = mfn_to_pfn(xen_start_info->store_mfn);
+ xen_start_info->console.domU.mfn =
+ mfn_to_pfn(xen_start_info->console.domU.mfn);
+}
+
+static void post_suspend(void)
+{
+ int i, j, k, fpp;
+ extern unsigned long max_pfn;
+ extern unsigned long *pfn_to_mfn_frame_list_list;
+ extern unsigned long *pfn_to_mfn_frame_list[];
+
+ set_fixmap(FIX_SHARED_INFO, xen_start_info->shared_info);
+
+ HYPERVISOR_shared_info = (shared_info_t *)fix_to_virt(FIX_SHARED_INFO);
+
+ memset(empty_zero_page, 0, PAGE_SIZE);
+
+ HYPERVISOR_shared_info->arch.pfn_to_mfn_frame_list_list =
+ virt_to_mfn(pfn_to_mfn_frame_list_list);
+
+ fpp = PAGE_SIZE/sizeof(unsigned long);
+ for (i = 0, j = 0, k = -1; i < max_pfn; i += fpp, j++) {
+ if ((j % fpp) == 0) {
+ k++;
+ pfn_to_mfn_frame_list_list[k] =
+ virt_to_mfn(pfn_to_mfn_frame_list[k]);
+ j = 0;
+ }
+ pfn_to_mfn_frame_list[k][j] =
+ virt_to_mfn(&phys_to_machine_mapping[i]);
+ }
+ HYPERVISOR_shared_info->arch.max_pfn = max_pfn;
+}
+
+#else /* !(defined(__i386__) || defined(__x86_64__)) */
+
+#define switch_idle_mm() ((void)0)
+#define mm_pin_all() ((void)0)
+#define pre_suspend() ((void)0)
+#define post_suspend() ((void)0)
+
+#endif
+
+int __xen_suspend(void)
+{
+ int err;
+
+ extern void time_resume(void);
+
+ BUG_ON(smp_processor_id() != 0);
+ BUG_ON(in_interrupt());
+
+#if defined(__i386__) || defined(__x86_64__)
+ if (xen_feature(XENFEAT_auto_translated_physmap)) {
+ printk(KERN_WARNING "Cannot suspend in "
+ "auto_translated_physmap mode.\n");
+ return -EOPNOTSUPP;
+ }
+#endif
+
+ err = smp_suspend();
+ if (err)
+ return err;
+
+ xenbus_suspend();
+
+ preempt_disable();
+
+ mm_pin_all();
+ local_irq_disable();
+ preempt_enable();
+
+ gnttab_suspend();
+
+ pre_suspend();
+
+ /*
+ * We'll stop somewhere inside this hypercall. When it returns,
+ * we'll start resuming after the restore.
+ */
+ HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
+
+ post_suspend();
+
+ gnttab_resume();
+
+ irq_resume();
+
+ time_resume();
+
+ switch_idle_mm();
+
+ local_irq_enable();
+
+ xencons_resume();
+
+ xenbus_resume();
+
+ smp_resume();
+
+ return err;
+}
diff --git a/linux-2.6-xen-sparse/drivers/xen/core/reboot.c b/linux-2.6-xen-sparse/drivers/xen/core/reboot.c
index 34c3930961..af3fe3a15c 100644
--- a/linux-2.6-xen-sparse/drivers/xen/core/reboot.c
+++ b/linux-2.6-xen-sparse/drivers/xen/core/reboot.c
@@ -1,25 +1,15 @@
#define __KERNEL_SYSCALLS__
#include <linux/version.h>
#include <linux/kernel.h>
-#include <linux/mm.h>
#include <linux/unistd.h>
#include <linux/module.h>
#include <linux/reboot.h>
#include <linux/sysrq.h>
-#include <linux/stringify.h>
-#include <asm/irq.h>
-#include <asm/mmu_context.h>
-#include <xen/evtchn.h>
#include <asm/hypervisor.h>
-#include <xen/interface/dom0_ops.h>
#include <xen/xenbus.h>
-#include <linux/cpu.h>
#include <linux/kthread.h>
-#include <xen/gnttab.h>
-#include <xen/xencons.h>
-#include <xen/cpu_hotplug.h>
-extern void ctrl_alt_del(void);
+MODULE_LICENSE("Dual BSD/GPL");
#define SHUTDOWN_INVALID -1
#define SHUTDOWN_POWEROFF 0
@@ -31,186 +21,18 @@ extern void ctrl_alt_del(void);
*/
#define SHUTDOWN_HALT 4
-#if defined(__i386__) || defined(__x86_64__)
-
-/*
- * Power off function, if any
- */
-void (*pm_power_off)(void);
-EXPORT_SYMBOL(pm_power_off);
-
-void machine_emergency_restart(void)
-{
- /* We really want to get pending console data out before we die. */
- xencons_force_flush();
- HYPERVISOR_shutdown(SHUTDOWN_reboot);
-}
-
-void machine_restart(char * __unused)
-{
- machine_emergency_restart();
-}
-
-void machine_halt(void)
-{
- machine_power_off();
-}
-
-void machine_power_off(void)
-{
- /* We really want to get pending console data out before we die. */
- xencons_force_flush();
- if (pm_power_off)
- pm_power_off();
- HYPERVISOR_shutdown(SHUTDOWN_poweroff);
-}
-
-int reboot_thru_bios = 0; /* for dmi_scan.c */
-EXPORT_SYMBOL(machine_restart);
-EXPORT_SYMBOL(machine_halt);
-EXPORT_SYMBOL(machine_power_off);
-
-#endif /* defined(__i386__) || defined(__x86_64__) */
-
-/******************************************************************************
- * Stop/pickle callback handling.
- */
-
/* Ignore multiple shutdown requests. */
static int shutting_down = SHUTDOWN_INVALID;
+
static void __shutdown_handler(void *unused);
static DECLARE_WORK(shutdown_work, __shutdown_handler, NULL);
-#if defined(__i386__) || defined(__x86_64__)
-
-/* Ensure we run on the idle task page tables so that we will
- switch page tables before running user space. This is needed
- on architectures with separate kernel and user page tables
- because the user page table pointer is not saved/restored. */
-static void switch_idle_mm(void)
-{
- struct mm_struct *mm = current->active_mm;
-
- if (mm == &init_mm)
- return;
-
- atomic_inc(&init_mm.mm_count);
- switch_mm(mm, &init_mm, current);
- current->active_mm = &init_mm;
- mmdrop(mm);
-}
-
-static void pre_suspend(void)
-{
- HYPERVISOR_shared_info = (shared_info_t *)empty_zero_page;
- clear_fixmap(FIX_SHARED_INFO);
-
- xen_start_info->store_mfn = mfn_to_pfn(xen_start_info->store_mfn);
- xen_start_info->console.domU.mfn =
- mfn_to_pfn(xen_start_info->console.domU.mfn);
-}
-
-static void post_suspend(void)
-{
- int i, j, k, fpp;
- extern unsigned long max_pfn;
- extern unsigned long *pfn_to_mfn_frame_list_list;
- extern unsigned long *pfn_to_mfn_frame_list[];
-
- set_fixmap(FIX_SHARED_INFO, xen_start_info->shared_info);
-
- HYPERVISOR_shared_info = (shared_info_t *)fix_to_virt(FIX_SHARED_INFO);
-
- memset(empty_zero_page, 0, PAGE_SIZE);
-
- HYPERVISOR_shared_info->arch.pfn_to_mfn_frame_list_list =
- virt_to_mfn(pfn_to_mfn_frame_list_list);
-
- fpp = PAGE_SIZE/sizeof(unsigned long);
- for (i = 0, j = 0, k = -1; i < max_pfn; i += fpp, j++) {
- if ((j % fpp) == 0) {
- k++;
- pfn_to_mfn_frame_list_list[k] =
- virt_to_mfn(pfn_to_mfn_frame_list[k]);
- j = 0;
- }
- pfn_to_mfn_frame_list[k][j] =
- virt_to_mfn(&phys_to_machine_mapping[i]);
- }
- HYPERVISOR_shared_info->arch.max_pfn = max_pfn;
-}
-
-#else /* !(defined(__i386__) || defined(__x86_64__)) */
-
-#define switch_idle_mm() ((void)0)
-#define mm_pin_all() ((void)0)
-#define pre_suspend() ((void)0)
-#define post_suspend() ((void)0)
-
-#endif
-
-static int __do_suspend(void *ignore)
-{
- int err;
-
- extern void time_resume(void);
-
- BUG_ON(smp_processor_id() != 0);
- BUG_ON(in_interrupt());
-
-#if defined(__i386__) || defined(__x86_64__)
- if (xen_feature(XENFEAT_auto_translated_physmap)) {
- printk(KERN_WARNING "Cannot suspend in "
- "auto_translated_physmap mode.\n");
- return -EOPNOTSUPP;
- }
+#ifdef CONFIG_XEN
+int __xen_suspend(void);
+#else
+#define __xen_suspend() (void)0
#endif
- err = smp_suspend();
- if (err)
- return err;
-
- xenbus_suspend();
-
- preempt_disable();
-
- mm_pin_all();
- local_irq_disable();
- preempt_enable();
-
- gnttab_suspend();
-
- pre_suspend();
-
- /*
- * We'll stop somewhere inside this hypercall. When it returns,
- * we'll start resuming after the restore.
- */
- HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
-
- shutting_down = SHUTDOWN_INVALID;
-
- post_suspend();
-
- gnttab_resume();
-
- irq_resume();
-
- time_resume();
-
- switch_idle_mm();
-
- local_irq_enable();
-
- xencons_resume();
-
- xenbus_resume();
-
- smp_resume();
-
- return err;
-}
-
static int shutdown_process(void *__unused)
{
static char *envp[] = { "HOME=/", "TERM=linux",
@@ -222,11 +44,13 @@ static int shutdown_process(void *__unused)
if ((shutting_down == SHUTDOWN_POWEROFF) ||
(shutting_down == SHUTDOWN_HALT)) {
- if (execve("/sbin/poweroff", poweroff_argv, envp) < 0) {
+ if (call_usermodehelper("/sbin/poweroff", poweroff_argv, envp, 0) < 0) {
+#ifdef CONFIG_XEN
sys_reboot(LINUX_REBOOT_MAGIC1,
LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_POWER_OFF,
NULL);
+#endif /* CONFIG_XEN */
}
}
@@ -235,6 +59,13 @@ static int shutdown_process(void *__unused)
return 0;
}
+static int xen_suspend(void *__unused)
+{
+ __xen_suspend();
+ shutting_down = SHUTDOWN_INVALID;
+ return 0;
+}
+
static int kthread_create_on_cpu(int (*f)(void *arg),
void *arg,
const char *name,
@@ -257,7 +88,7 @@ static void __shutdown_handler(void *unused)
err = kernel_thread(shutdown_process, NULL,
CLONE_FS | CLONE_FILES);
else
- err = kthread_create_on_cpu(__do_suspend, NULL, "suspend", 0);
+ err = kthread_create_on_cpu(xen_suspend, NULL, "suspend", 0);
if (err < 0) {
printk(KERN_WARNING "Error creating shutdown process (%d): "
@@ -298,7 +129,7 @@ static void shutdown_handler(struct xenbus_watch *watch,
if (strcmp(str, "poweroff") == 0)
shutting_down = SHUTDOWN_POWEROFF;
else if (strcmp(str, "reboot") == 0)
- ctrl_alt_del();
+ kill_proc(1, SIGINT, 1); /* interrupt init */
else if (strcmp(str, "suspend") == 0)
shutting_down = SHUTDOWN_SUSPEND;
else if (strcmp(str, "halt") == 0)
@@ -364,10 +195,14 @@ static int setup_shutdown_watcher(struct notifier_block *notifier,
err = register_xenbus_watch(&shutdown_watch);
if (err)
printk(KERN_ERR "Failed to set shutdown watcher\n");
+ else
+ xenbus_write(XBT_NIL, "control", "feature-reboot", "1");
err = register_xenbus_watch(&sysrq_watch);
if (err)
printk(KERN_ERR "Failed to set sysrq watcher\n");
+ else
+ xenbus_write(XBT_NIL, "control", "feature-sysrq", "1");
return NOTIFY_DONE;
}
@@ -378,6 +213,7 @@ static int __init setup_shutdown_event(void)
.notifier_call = setup_shutdown_watcher
};
register_xenstore_notifier(&xenstore_notifier);
+
return 0;
}
diff --git a/linux-2.6-xen-sparse/drivers/xen/netback/interface.c b/linux-2.6-xen-sparse/drivers/xen/netback/interface.c
index c56013945b..9fae954bd2 100644
--- a/linux-2.6-xen-sparse/drivers/xen/netback/interface.c
+++ b/linux-2.6-xen-sparse/drivers/xen/netback/interface.c
@@ -166,9 +166,6 @@ netif_t *netif_alloc(domid_t domid, unsigned int handle)
SET_ETHTOOL_OPS(dev, &network_ethtool_ops);
dev->tx_queue_len = netbk_queue_length;
- if (dev->tx_queue_len != 0)
- printk(KERN_WARNING "netbk: WARNING: device '%s' has non-zero "
- "queue length (%lu)!\n", dev->name, dev->tx_queue_len);
/*
* Initialise a dummy MAC address. We choose the numerically
diff --git a/linux-2.6-xen-sparse/drivers/xen/netback/netback.c b/linux-2.6-xen-sparse/drivers/xen/netback/netback.c
index 5706322a8f..c4fb3e4383 100644
--- a/linux-2.6-xen-sparse/drivers/xen/netback/netback.c
+++ b/linux-2.6-xen-sparse/drivers/xen/netback/netback.c
@@ -814,7 +814,7 @@ void netif_deschedule_work(netif_t *netif)
static void tx_add_credit(netif_t *netif)
{
- unsigned long max_burst;
+ unsigned long max_burst, max_credit;
/*
* Allow a burst big enough to transmit a jumbo packet of up to 128kB.
@@ -824,9 +824,10 @@ static void tx_add_credit(netif_t *netif)
max_burst = min(max_burst, 131072UL);
max_burst = max(max_burst, netif->credit_bytes);
- netif->remaining_credit = min(netif->remaining_credit +
- netif->credit_bytes,
- max_burst);
+ /* Take care that adding a new chunk of credit doesn't wrap to zero. */
+ max_credit = max(netif->remaining_credit + netif->credit_bytes, ~0UL);
+
+ netif->remaining_credit = min(max_credit, max_burst);
}
static void tx_credit_callback(unsigned long data)
diff --git a/linux-2.6-xen-sparse/drivers/xen/netback/xenbus.c b/linux-2.6-xen-sparse/drivers/xen/netback/xenbus.c
index b81042d701..7d301965f4 100644
--- a/linux-2.6-xen-sparse/drivers/xen/netback/xenbus.c
+++ b/linux-2.6-xen-sparse/drivers/xen/netback/xenbus.c
@@ -93,10 +93,22 @@ static int netback_probe(struct xenbus_device *dev,
goto abort_transaction;
}
+ /* We support rx-copy path. */
err = xenbus_printf(xbt, dev->nodename,
"feature-rx-copy", "%d", 1);
if (err) {
- message = "writing feature-copying";
+ message = "writing feature-rx-copy";
+ goto abort_transaction;
+ }
+
+ /*
+ * We don't support rx-flip path (except old guests who don't
+ * grok this feature flag).
+ */
+ err = xenbus_printf(xbt, dev->nodename,
+ "feature-rx-flip", "%d", 0);
+ if (err) {
+ message = "writing feature-rx-flip";
goto abort_transaction;
}
diff --git a/linux-2.6-xen-sparse/drivers/xen/netfront/netfront.c b/linux-2.6-xen-sparse/drivers/xen/netfront/netfront.c
index e03e44a05a..da22d45bf6 100644
--- a/linux-2.6-xen-sparse/drivers/xen/netfront/netfront.c
+++ b/linux-2.6-xen-sparse/drivers/xen/netfront/netfront.c
@@ -101,6 +101,14 @@ static inline void dev_disable_gso_features(struct net_device *dev)
}
#elif defined(NETIF_F_TSO)
#define HAVE_TSO 1
+
+/* Some older kernels cannot cope with incorrect checksums,
+ * particularly in netfilter. I'm not sure there is 100% correlation
+ * with the presence of NETIF_F_TSO but it appears to be a good first
+ * approximiation.
+ */
+#define HAVE_NO_CSUM_OFFLOAD 1
+
#define gso_size tso_size
#define gso_segs tso_segs
static inline void dev_disable_gso_features(struct net_device *dev)
@@ -242,7 +250,6 @@ static void end_access(int, void *);
static void netif_disconnect_backend(struct netfront_info *);
static int open_netdev(struct netfront_info *);
static void close_netdev(struct netfront_info *);
-static void netif_free(struct netfront_info *);
static int network_connect(struct net_device *);
static void network_tx_buf_gc(struct net_device *);
@@ -395,6 +402,14 @@ again:
goto abort_transaction;
}
+#ifdef HAVE_NO_CSUM_OFFLOAD
+ err = xenbus_printf(xbt, dev->nodename, "feature-no-csum-offload", "%d", 1);
+ if (err) {
+ message = "writing feature-no-csum-offload";
+ goto abort_transaction;
+ }
+#endif
+
err = xenbus_printf(xbt, dev->nodename, "feature-sg", "%d", 1);
if (err) {
message = "writing feature-sg";
@@ -428,7 +443,6 @@ again:
return err;
}
-
static int setup_device(struct xenbus_device *dev, struct netfront_info *info)
{
struct netif_tx_sring *txs;
@@ -488,11 +502,9 @@ static int setup_device(struct xenbus_device *dev, struct netfront_info *info)
return 0;
fail:
- netif_free(info);
return err;
}
-
/**
* Callback received when the backend's state changes.
*/
@@ -513,10 +525,8 @@ static void backend_changed(struct xenbus_device *dev,
break;
case XenbusStateInitWait:
- if (network_connect(netdev) != 0) {
- netif_free(np);
+ if (network_connect(netdev) != 0)
break;
- }
xenbus_switch_state(dev, XenbusStateConnected);
(void)send_fake_arp(netdev);
break;
@@ -527,7 +537,6 @@ static void backend_changed(struct xenbus_device *dev,
}
}
-
/** Send a packet on a net device to encourage switches to learn the
* MAC. We send a fake ARP request.
*
@@ -556,7 +565,6 @@ static int send_fake_arp(struct net_device *dev)
return dev_queue_xmit(skb);
}
-
static int network_open(struct net_device *dev)
{
struct netfront_info *np = netdev_priv(dev);
@@ -648,14 +656,12 @@ static void network_tx_buf_gc(struct net_device *dev)
network_maybe_wake_tx(dev);
}
-
static void rx_refill_timeout(unsigned long data)
{
struct net_device *dev = (struct net_device *)data;
netif_rx_schedule(dev);
}
-
static void network_alloc_rx_buffers(struct net_device *dev)
{
unsigned short id;
@@ -1617,8 +1623,16 @@ static void xennet_set_features(struct net_device *dev)
if (!(dev->features & NETIF_F_IP_CSUM))
return;
- if (!xennet_set_sg(dev, 1))
- xennet_set_tso(dev, 1);
+ if (xennet_set_sg(dev, 1))
+ return;
+
+ /* Before 2.6.9 TSO seems to be unreliable so do not enable it
+ * on older kernels.
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,9)
+ xennet_set_tso(dev, 1);
+#endif
+
}
static int network_connect(struct net_device *dev)
@@ -2063,14 +2077,6 @@ static void netif_disconnect_backend(struct netfront_info *info)
}
-static void netif_free(struct netfront_info *info)
-{
- close_netdev(info);
- netif_disconnect_backend(info);
- free_netdev(info->netdev);
-}
-
-
static void end_access(int ref, void *page)
{
if (ref != GRANT_INVALID_REF)
diff --git a/linux-2.6-xen-sparse/drivers/xen/privcmd/privcmd.c b/linux-2.6-xen-sparse/drivers/xen/privcmd/privcmd.c
index a65b8744c2..860e5f9ec9 100644
--- a/linux-2.6-xen-sparse/drivers/xen/privcmd/privcmd.c
+++ b/linux-2.6-xen-sparse/drivers/xen/privcmd/privcmd.c
@@ -53,6 +53,8 @@ static int privcmd_ioctl(struct inode *inode, struct file *file,
return -EFAULT;
#if defined(__i386__)
+ if (hypercall.op >= (PAGE_SIZE >> 5))
+ break;
__asm__ __volatile__ (
"pushl %%ebx; pushl %%ecx; pushl %%edx; "
"pushl %%esi; pushl %%edi; "
@@ -69,21 +71,21 @@ static int privcmd_ioctl(struct inode *inode, struct file *file,
"popl %%ecx; popl %%ebx"
: "=a" (ret) : "0" (&hypercall) : "memory" );
#elif defined (__x86_64__)
- {
+ if (hypercall.op < (PAGE_SIZE >> 5)) {
long ign1, ign2, ign3;
__asm__ __volatile__ (
"movq %8,%%r10; movq %9,%%r8;"
- "shlq $5,%%rax ;"
+ "shll $5,%%eax ;"
"addq $hypercall_page,%%rax ;"
"call *%%rax"
: "=a" (ret), "=D" (ign1),
"=S" (ign2), "=d" (ign3)
- : "0" ((unsigned long)hypercall.op),
- "1" ((unsigned long)hypercall.arg[0]),
- "2" ((unsigned long)hypercall.arg[1]),
- "3" ((unsigned long)hypercall.arg[2]),
- "g" ((unsigned long)hypercall.arg[3]),
- "g" ((unsigned long)hypercall.arg[4])
+ : "0" ((unsigned int)hypercall.op),
+ "1" (hypercall.arg[0]),
+ "2" (hypercall.arg[1]),
+ "3" (hypercall.arg[2]),
+ "g" (hypercall.arg[3]),
+ "g" (hypercall.arg[4])
: "r8", "r10", "memory" );
}
#elif defined (__ia64__)
diff --git a/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_comms.c b/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_comms.c
index ea8f3c283e..f0e42ba715 100644
--- a/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_comms.c
+++ b/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_comms.c
@@ -30,13 +30,16 @@
* IN THE SOFTWARE.
*/
-#include <asm/hypervisor.h>
-#include <xen/evtchn.h>
#include <linux/wait.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/err.h>
+#include <linux/ptrace.h>
+#include <xen/evtchn.h>
#include <xen/xenbus.h>
+
+#include <asm/hypervisor.h>
+
#include "xenbus_comms.h"
#ifdef HAVE_XEN_PLATFORM_COMPAT_H
diff --git a/linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/fixmap.h b/linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/fixmap.h
index a6f3e9ea79..992a7c13ed 100644
--- a/linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/fixmap.h
+++ b/linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/fixmap.h
@@ -27,7 +27,6 @@ extern unsigned long __FIXADDR_TOP;
#include <asm/acpi.h>
#include <asm/apicdef.h>
#include <asm/page.h>
-#include <xen/gnttab.h>
#ifdef CONFIG_HIGHMEM
#include <linux/threads.h>
#include <asm/kmap_types.h>
diff --git a/linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/hypercall.h b/linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/hypercall.h
index 2e6d1fa596..a12e349016 100644
--- a/linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/hypercall.h
+++ b/linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/hypercall.h
@@ -260,6 +260,8 @@ HYPERVISOR_event_channel_op(
int cmd, void *arg)
{
int rc = _hypercall2(int, event_channel_op, cmd, arg);
+
+#ifdef CONFIG_XEN_COMPAT_030002
if (unlikely(rc == -ENOSYS)) {
struct evtchn_op op;
op.cmd = cmd;
@@ -267,6 +269,8 @@ HYPERVISOR_event_channel_op(
rc = _hypercall1(int, event_channel_op_compat, &op);
memcpy(arg, &op.u, sizeof(op.u));
}
+#endif
+
return rc;
}
@@ -296,6 +300,8 @@ HYPERVISOR_physdev_op(
int cmd, void *arg)
{
int rc = _hypercall2(int, physdev_op, cmd, arg);
+
+#ifdef CONFIG_XEN_COMPAT_030002
if (unlikely(rc == -ENOSYS)) {
struct physdev_op op;
op.cmd = cmd;
@@ -303,6 +309,8 @@ HYPERVISOR_physdev_op(
rc = _hypercall1(int, physdev_op_compat, &op);
memcpy(arg, &op.u, sizeof(op.u));
}
+#endif
+
return rc;
}
@@ -350,9 +358,11 @@ HYPERVISOR_suspend(
int rc = _hypercall3(int, sched_op, SCHEDOP_shutdown,
&sched_shutdown, srec);
+#ifdef CONFIG_XEN_COMPAT_030002
if (rc == -ENOSYS)
rc = _hypercall3(int, sched_op_compat, SCHEDOP_shutdown,
SHUTDOWN_suspend, srec);
+#endif
return rc;
}
diff --git a/linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/hypervisor.h b/linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/hypervisor.h
index 47586d22f9..a037eb24e4 100644
--- a/linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/hypervisor.h
+++ b/linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/hypervisor.h
@@ -131,8 +131,10 @@ HYPERVISOR_yield(
{
int rc = HYPERVISOR_sched_op(SCHEDOP_yield, NULL);
+#ifdef CONFIG_XEN_COMPAT_030002
if (rc == -ENOSYS)
rc = HYPERVISOR_sched_op_compat(SCHEDOP_yield, 0);
+#endif
return rc;
}
@@ -143,8 +145,10 @@ HYPERVISOR_block(
{
int rc = HYPERVISOR_sched_op(SCHEDOP_block, NULL);
+#ifdef CONFIG_XEN_COMPAT_030002
if (rc == -ENOSYS)
rc = HYPERVISOR_sched_op_compat(SCHEDOP_block, 0);
+#endif
return rc;
}
@@ -159,8 +163,10 @@ HYPERVISOR_shutdown(
int rc = HYPERVISOR_sched_op(SCHEDOP_shutdown, &sched_shutdown);
+#ifdef CONFIG_XEN_COMPAT_030002
if (rc == -ENOSYS)
rc = HYPERVISOR_sched_op_compat(SCHEDOP_shutdown, reason);
+#endif
return rc;
}
@@ -177,8 +183,10 @@ HYPERVISOR_poll(
set_xen_guest_handle(sched_poll.ports, ports);
rc = HYPERVISOR_sched_op(SCHEDOP_poll, &sched_poll);
+#ifdef CONFIG_XEN_COMPAT_030002
if (rc == -ENOSYS)
rc = HYPERVISOR_sched_op_compat(SCHEDOP_yield, 0);
+#endif
return rc;
}
diff --git a/linux-2.6-xen-sparse/include/asm-i386/mach-xen/setup_arch_post.h b/linux-2.6-xen-sparse/include/asm-i386/mach-xen/setup_arch_post.h
index 0f1caa0604..7314dedbde 100644
--- a/linux-2.6-xen-sparse/include/asm-i386/mach-xen/setup_arch_post.h
+++ b/linux-2.6-xen-sparse/include/asm-i386/mach-xen/setup_arch_post.h
@@ -56,15 +56,15 @@ static void __init machine_specific_arch_setup(void)
struct xen_machphys_mapping mapping;
unsigned long machine_to_phys_nr_ents;
struct xen_platform_parameters pp;
- struct callback_register event = {
+ static struct callback_register __initdata event = {
.type = CALLBACKTYPE_event,
.address = { __KERNEL_CS, (unsigned long)hypervisor_callback },
};
- struct callback_register failsafe = {
+ static struct callback_register __initdata failsafe = {
.type = CALLBACKTYPE_failsafe,
.address = { __KERNEL_CS, (unsigned long)failsafe_callback },
};
- struct callback_register nmi_cb = {
+ static struct callback_register __initdata nmi_cb = {
.type = CALLBACKTYPE_nmi,
.address = { __KERNEL_CS, (unsigned long)nmi },
};
@@ -72,19 +72,24 @@ static void __init machine_specific_arch_setup(void)
ret = HYPERVISOR_callback_op(CALLBACKOP_register, &event);
if (ret == 0)
ret = HYPERVISOR_callback_op(CALLBACKOP_register, &failsafe);
+#ifdef CONFIG_XEN_COMPAT_030002
if (ret == -ENOSYS)
ret = HYPERVISOR_set_callbacks(
event.address.cs, event.address.eip,
failsafe.address.cs, failsafe.address.eip);
+#endif
BUG_ON(ret);
ret = HYPERVISOR_callback_op(CALLBACKOP_register, &nmi_cb);
+#ifdef CONFIG_XEN_COMPAT_030002
if (ret == -ENOSYS) {
- struct xennmi_callback cb;
+ static struct xennmi_callback __initdata cb = {
+ .handler_address = (unsigned long)nmi
+ };
- cb.handler_address = nmi_cb.address.eip;
HYPERVISOR_nmi_op(XENNMI_register_callback, &cb);
}
+#endif
if (HYPERVISOR_xen_version(XENVER_platform_parameters,
&pp) == 0)
diff --git a/linux-2.6-xen-sparse/include/asm-ia64/hypercall.h b/linux-2.6-xen-sparse/include/asm-ia64/hypercall.h
index 43f88b1930..7a522be483 100644
--- a/linux-2.6-xen-sparse/include/asm-ia64/hypercall.h
+++ b/linux-2.6-xen-sparse/include/asm-ia64/hypercall.h
@@ -283,6 +283,9 @@ static inline void exit_idle(void) {}
#ifdef CONFIG_XEN
#include <asm/xen/privop.h>
#endif /* CONFIG_XEN */
+#ifdef HAVE_XEN_PLATFORM_COMPAT_H
+#include <xen/platform-compat.h>
+#endif
static inline unsigned long
__HYPERVISOR_ioremap(unsigned long ioaddr, unsigned long size)
diff --git a/linux-2.6-xen-sparse/include/asm-ia64/hypervisor.h b/linux-2.6-xen-sparse/include/asm-ia64/hypervisor.h
index 43db0a2367..083884c130 100644
--- a/linux-2.6-xen-sparse/include/asm-ia64/hypervisor.h
+++ b/linux-2.6-xen-sparse/include/asm-ia64/hypervisor.h
@@ -191,6 +191,22 @@ MULTI_grant_table_op(multicall_entry_t *mcl, unsigned int cmd,
mcl->args[2] = count;
}
+/*
+ * for blktap.c
+ * int create_lookup_pte_addr(struct mm_struct *mm,
+ * unsigned long address,
+ * uint64_t *ptep);
+ */
+#define create_lookup_pte_addr(mm, address, ptep) \
+ ({ \
+ printk(KERN_EMERG \
+ "%s:%d " \
+ "create_lookup_pte_addr() isn't supported.\n", \
+ __func__, __LINE__); \
+ BUG(); \
+ (-ENOSYS); \
+ })
+
// for debug
asmlinkage int xprintk(const char *fmt, ...);
#define xprintd(fmt, ...) xprintk("%s:%d " fmt, __func__, __LINE__, \
diff --git a/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/fixmap.h b/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/fixmap.h
index 64ae42e1aa..29beed9512 100644
--- a/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/fixmap.h
+++ b/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/fixmap.h
@@ -14,7 +14,6 @@
#include <linux/config.h>
#include <linux/kernel.h>
#include <asm/apicdef.h>
-#include <xen/gnttab.h>
#include <asm/page.h>
#include <asm/vsyscall.h>
#include <asm/vsyscall32.h>
diff --git a/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/hypercall.h b/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/hypercall.h
index 14fb01d3d2..956a4c4b0d 100644
--- a/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/hypercall.h
+++ b/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/hypercall.h
@@ -258,6 +258,8 @@ HYPERVISOR_event_channel_op(
int cmd, void *arg)
{
int rc = _hypercall2(int, event_channel_op, cmd, arg);
+
+#ifdef CONFIG_XEN_COMPAT_030002
if (unlikely(rc == -ENOSYS)) {
struct evtchn_op op;
op.cmd = cmd;
@@ -265,6 +267,8 @@ HYPERVISOR_event_channel_op(
rc = _hypercall1(int, event_channel_op_compat, &op);
memcpy(arg, &op.u, sizeof(op.u));
}
+#endif
+
return rc;
}
@@ -294,6 +298,8 @@ HYPERVISOR_physdev_op(
int cmd, void *arg)
{
int rc = _hypercall2(int, physdev_op, cmd, arg);
+
+#ifdef CONFIG_XEN_COMPAT_030002
if (unlikely(rc == -ENOSYS)) {
struct physdev_op op;
op.cmd = cmd;
@@ -301,6 +307,8 @@ HYPERVISOR_physdev_op(
rc = _hypercall1(int, physdev_op_compat, &op);
memcpy(arg, &op.u, sizeof(op.u));
}
+#endif
+
return rc;
}
@@ -351,9 +359,11 @@ HYPERVISOR_suspend(
int rc = _hypercall3(int, sched_op, SCHEDOP_shutdown,
&sched_shutdown, srec);
+#ifdef CONFIG_XEN_COMPAT_030002
if (rc == -ENOSYS)
rc = _hypercall3(int, sched_op_compat, SCHEDOP_shutdown,
SHUTDOWN_suspend, srec);
+#endif
return rc;
}
diff --git a/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/setup_arch_post.h b/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/setup_arch_post.h
index e21d4ee6f2..5244e2855e 100644
--- a/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/setup_arch_post.h
+++ b/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/setup_arch_post.h
@@ -15,20 +15,20 @@ extern void nmi(void);
static void __init machine_specific_arch_setup(void)
{
int ret;
- struct callback_register event = {
+ static struct callback_register __initdata event = {
.type = CALLBACKTYPE_event,
.address = (unsigned long) hypervisor_callback,
};
- struct callback_register failsafe = {
+ static struct callback_register __initdata failsafe = {
.type = CALLBACKTYPE_failsafe,
.address = (unsigned long)failsafe_callback,
};
- struct callback_register syscall = {
+ static struct callback_register __initdata syscall = {
.type = CALLBACKTYPE_syscall,
.address = (unsigned long)system_call,
};
#ifdef CONFIG_X86_LOCAL_APIC
- struct callback_register nmi_cb = {
+ static struct callback_register __initdata nmi_cb = {
.type = CALLBACKTYPE_nmi,
.address = (unsigned long)nmi,
};
@@ -39,20 +39,25 @@ static void __init machine_specific_arch_setup(void)
ret = HYPERVISOR_callback_op(CALLBACKOP_register, &failsafe);
if (ret == 0)
ret = HYPERVISOR_callback_op(CALLBACKOP_register, &syscall);
+#ifdef CONFIG_XEN_COMPAT_030002
if (ret == -ENOSYS)
ret = HYPERVISOR_set_callbacks(
event.address,
failsafe.address,
syscall.address);
+#endif
BUG_ON(ret);
#ifdef CONFIG_X86_LOCAL_APIC
ret = HYPERVISOR_callback_op(CALLBACKOP_register, &nmi_cb);
+#ifdef CONFIG_XEN_COMPAT_030002
if (ret == -ENOSYS) {
- struct xennmi_callback cb;
+ static struct xennmi_callback __initdata cb = {
+ .handler_address = (unsigned long)nmi
+ };
- cb.handler_address = nmi_cb.address;
HYPERVISOR_nmi_op(XENNMI_register_callback, &cb);
}
#endif
+#endif
}
diff --git a/linux-2.6-xen-sparse/include/xen/gnttab.h b/linux-2.6-xen-sparse/include/xen/gnttab.h
index 676fca5054..f1543c01d6 100644
--- a/linux-2.6-xen-sparse/include/xen/gnttab.h
+++ b/linux-2.6-xen-sparse/include/xen/gnttab.h
@@ -39,6 +39,7 @@
#include <linux/config.h>
#include <asm/hypervisor.h>
+#include <asm/maddr.h> /* maddr_t */
#include <xen/interface/grant_table.h>
#include <xen/features.h>
@@ -118,7 +119,7 @@ int gnttab_suspend(void);
int gnttab_resume(void);
static inline void
-gnttab_set_map_op(struct gnttab_map_grant_ref *map, unsigned long addr,
+gnttab_set_map_op(struct gnttab_map_grant_ref *map, maddr_t addr,
uint32_t flags, grant_ref_t ref, domid_t domid)
{
if (flags & GNTMAP_contains_pte)
@@ -134,7 +135,7 @@ gnttab_set_map_op(struct gnttab_map_grant_ref *map, unsigned long addr,
}
static inline void
-gnttab_set_unmap_op(struct gnttab_unmap_grant_ref *unmap, unsigned long addr,
+gnttab_set_unmap_op(struct gnttab_unmap_grant_ref *unmap, maddr_t addr,
uint32_t flags, grant_handle_t handle)
{
if (flags & GNTMAP_contains_pte)
diff --git a/patches/linux-2.6.16.29/series b/patches/linux-2.6.16.29/series
index 02d3751083..ae8fbd2885 100644
--- a/patches/linux-2.6.16.29/series
+++ b/patches/linux-2.6.16.29/series
@@ -10,6 +10,7 @@ net-gso-1-check-dodgy.patch
net-gso-2-checksum-fix.patch
net-gso-3-fix-errorcheck.patch
net-gso-4-kill-warnon.patch
+net-gso-5-rcv-mss.patch
pci-mmconfig-fix-from-2.6.17.patch
pmd-shared.patch
rcu_needs_cpu.patch
diff --git a/patches/linux-2.6.16.29/xenoprof-generic.patch b/patches/linux-2.6.16.29/xenoprof-generic.patch
index 6521fd7c7d..7783495f70 100644
--- a/patches/linux-2.6.16.29/xenoprof-generic.patch
+++ b/patches/linux-2.6.16.29/xenoprof-generic.patch
@@ -1,6 +1,6 @@
diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/buffer_sync.c ./drivers/oprofile/buffer_sync.c
---- ../orig-linux-2.6.16.29/drivers/oprofile/buffer_sync.c 2006-09-12 19:02:10.000000000 +0100
-+++ ./drivers/oprofile/buffer_sync.c 2006-09-19 14:06:05.000000000 +0100
+--- ../orig-linux-2.6.16.29/drivers/oprofile/buffer_sync.c 2006-11-06 14:46:52.000000000 -0800
++++ ./drivers/oprofile/buffer_sync.c 2006-11-06 15:16:52.000000000 -0800
@@ -6,6 +6,10 @@
*
* @author John Levon <levon@movementarian.org>
@@ -12,7 +12,27 @@ diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/buffer_sync.c ./drivers/opro
* This is the core of the buffer management. Each
* CPU buffer is processed and entered into the
* global event buffer. Such processing is necessary
-@@ -275,15 +279,31 @@ static void add_cpu_switch(int i)
+@@ -38,6 +42,7 @@ static cpumask_t marked_cpus = CPU_MASK_
+ static DEFINE_SPINLOCK(task_mortuary);
+ static void process_task_mortuary(void);
+
++static int cpu_current_domain[NR_CPUS];
+
+ /* Take ownership of the task struct and place it on the
+ * list for processing. Only after two full buffer syncs
+@@ -146,6 +151,11 @@ static void end_sync(void)
+ int sync_start(void)
+ {
+ int err;
++ int i;
++
++ for (i = 0; i < NR_CPUS; i++) {
++ cpu_current_domain[i] = COORDINATOR_DOMAIN;
++ }
+
+ start_cpu_work();
+
+@@ -275,15 +285,31 @@ static void add_cpu_switch(int i)
last_cookie = INVALID_COOKIE;
}
@@ -50,7 +70,7 @@ diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/buffer_sync.c ./drivers/opro
static void
add_user_ctx_switch(struct task_struct const * task, unsigned long cookie)
{
-@@ -348,9 +368,9 @@ static int add_us_sample(struct mm_struc
+@@ -348,9 +374,9 @@ static int add_us_sample(struct mm_struc
* for later lookup from userspace.
*/
static int
@@ -62,7 +82,7 @@ diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/buffer_sync.c ./drivers/opro
add_sample_entry(s->eip, s->event);
return 1;
} else if (mm) {
-@@ -496,10 +516,11 @@ void sync_buffer(int cpu)
+@@ -496,15 +522,21 @@ void sync_buffer(int cpu)
struct mm_struct *mm = NULL;
struct task_struct * new;
unsigned long cookie = 0;
@@ -75,7 +95,17 @@ diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/buffer_sync.c ./drivers/opro
down(&buffer_sem);
-@@ -512,16 +533,18 @@ void sync_buffer(int cpu)
+ add_cpu_switch(cpu);
+
++ /* We need to assign the first samples in this CPU buffer to the
++ same domain that we were processing at the last sync_buffer */
++ if (cpu_current_domain[cpu] != COORDINATOR_DOMAIN) {
++ add_domain_switch(cpu_current_domain[cpu]);
++ }
+ /* Remember, only we can modify tail_pos */
+
+ available = get_slots(cpu_buf);
+@@ -512,16 +544,18 @@ void sync_buffer(int cpu)
for (i = 0; i < available; ++i) {
struct op_sample * s = &cpu_buf->buffer[cpu_buf->tail_pos];
@@ -99,7 +129,7 @@ diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/buffer_sync.c ./drivers/opro
} else {
struct mm_struct * oldmm = mm;
-@@ -535,11 +558,16 @@ void sync_buffer(int cpu)
+@@ -535,11 +569,21 @@ void sync_buffer(int cpu)
add_user_ctx_switch(new, cookie);
}
} else {
@@ -109,10 +139,15 @@ diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/buffer_sync.c ./drivers/opro
- state = sb_bt_ignore;
- atomic_inc(&oprofile_stats.bt_lost_no_mapping);
+ if (domain_switch) {
++ cpu_current_domain[cpu] = s->eip;
+ add_domain_switch(s->eip);
+ domain_switch = 0;
+ } else {
-+ if (state >= sb_bt_start &&
++ if (cpu_current_domain[cpu] !=
++ COORDINATOR_DOMAIN) {
++ add_sample_entry(s->eip, s->event);
++ }
++ else if (state >= sb_bt_start &&
+ !add_sample(mm, s, cpu_mode)) {
+ if (state == sb_bt_start) {
+ state = sb_bt_ignore;
@@ -121,9 +156,21 @@ diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/buffer_sync.c ./drivers/opro
}
}
}
+@@ -548,6 +592,11 @@ void sync_buffer(int cpu)
+ }
+ release_mm(mm);
+
++ /* We reset domain to COORDINATOR at each CPU switch */
++ if (cpu_current_domain[cpu] != COORDINATOR_DOMAIN) {
++ add_domain_switch(COORDINATOR_DOMAIN);
++ }
++
+ mark_done(cpu);
+
+ up(&buffer_sem);
diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/cpu_buffer.c ./drivers/oprofile/cpu_buffer.c
---- ../orig-linux-2.6.16.29/drivers/oprofile/cpu_buffer.c 2006-09-12 19:02:10.000000000 +0100
-+++ ./drivers/oprofile/cpu_buffer.c 2006-09-19 14:06:05.000000000 +0100
+--- ../orig-linux-2.6.16.29/drivers/oprofile/cpu_buffer.c 2006-11-06 14:46:52.000000000 -0800
++++ ./drivers/oprofile/cpu_buffer.c 2006-11-06 14:47:55.000000000 -0800
@@ -6,6 +6,10 @@
*
* @author John Levon <levon@movementarian.org>
@@ -233,8 +280,8 @@ diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/cpu_buffer.c ./drivers/oprof
* This serves to avoid cpu buffer overflow, and makes sure
* the task mortuary progresses
diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/cpu_buffer.h ./drivers/oprofile/cpu_buffer.h
---- ../orig-linux-2.6.16.29/drivers/oprofile/cpu_buffer.h 2006-09-12 19:02:10.000000000 +0100
-+++ ./drivers/oprofile/cpu_buffer.h 2006-09-19 14:06:05.000000000 +0100
+--- ../orig-linux-2.6.16.29/drivers/oprofile/cpu_buffer.h 2006-11-06 14:46:52.000000000 -0800
++++ ./drivers/oprofile/cpu_buffer.h 2006-11-06 14:47:55.000000000 -0800
@@ -36,7 +36,7 @@ struct oprofile_cpu_buffer {
volatile unsigned long tail_pos;
unsigned long buffer_size;
@@ -258,8 +305,8 @@ diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/cpu_buffer.h ./drivers/oprof
#endif /* OPROFILE_CPU_BUFFER_H */
diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/event_buffer.h ./drivers/oprofile/event_buffer.h
---- ../orig-linux-2.6.16.29/drivers/oprofile/event_buffer.h 2006-09-12 19:02:10.000000000 +0100
-+++ ./drivers/oprofile/event_buffer.h 2006-09-19 14:06:05.000000000 +0100
+--- ../orig-linux-2.6.16.29/drivers/oprofile/event_buffer.h 2006-11-06 14:46:52.000000000 -0800
++++ ./drivers/oprofile/event_buffer.h 2006-11-06 14:47:55.000000000 -0800
@@ -29,15 +29,20 @@ void wake_up_buffer_waiter(void);
#define CPU_SWITCH_CODE 2
#define COOKIE_SWITCH_CODE 3
@@ -283,8 +330,8 @@ diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/event_buffer.h ./drivers/opr
void add_event_entry(unsigned long data);
diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/oprof.c ./drivers/oprofile/oprof.c
---- ../orig-linux-2.6.16.29/drivers/oprofile/oprof.c 2006-09-12 19:02:10.000000000 +0100
-+++ ./drivers/oprofile/oprof.c 2006-09-19 14:06:05.000000000 +0100
+--- ../orig-linux-2.6.16.29/drivers/oprofile/oprof.c 2006-11-06 14:46:52.000000000 -0800
++++ ./drivers/oprofile/oprof.c 2006-11-06 14:47:55.000000000 -0800
@@ -5,6 +5,10 @@
* @remark Read the file COPYING
*
@@ -339,8 +386,8 @@ diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/oprof.c ./drivers/oprofile/o
{
int err;
diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/oprof.h ./drivers/oprofile/oprof.h
---- ../orig-linux-2.6.16.29/drivers/oprofile/oprof.h 2006-09-12 19:02:10.000000000 +0100
-+++ ./drivers/oprofile/oprof.h 2006-09-19 14:06:05.000000000 +0100
+--- ../orig-linux-2.6.16.29/drivers/oprofile/oprof.h 2006-11-06 14:46:52.000000000 -0800
++++ ./drivers/oprofile/oprof.h 2006-11-06 14:47:55.000000000 -0800
@@ -35,5 +35,8 @@ void oprofile_create_files(struct super_
void oprofile_timer_init(struct oprofile_operations * ops);
@@ -351,8 +398,8 @@ diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/oprof.h ./drivers/oprofile/o
#endif /* OPROF_H */
diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/oprofile_files.c ./drivers/oprofile/oprofile_files.c
---- ../orig-linux-2.6.16.29/drivers/oprofile/oprofile_files.c 2006-09-12 19:02:10.000000000 +0100
-+++ ./drivers/oprofile/oprofile_files.c 2006-09-19 14:06:05.000000000 +0100
+--- ../orig-linux-2.6.16.29/drivers/oprofile/oprofile_files.c 2006-11-06 14:46:52.000000000 -0800
++++ ./drivers/oprofile/oprofile_files.c 2006-11-06 14:47:55.000000000 -0800
@@ -5,15 +5,21 @@
* @remark Read the file COPYING
*
@@ -581,8 +628,8 @@ diff -pruN ../orig-linux-2.6.16.29/drivers/oprofile/oprofile_files.c ./drivers/o
oprofilefs_create_ulong(sb, root, "buffer_size", &fs_buffer_size);
oprofilefs_create_ulong(sb, root, "buffer_watershed", &fs_buffer_watershed);
diff -pruN ../orig-linux-2.6.16.29/include/linux/oprofile.h ./include/linux/oprofile.h
---- ../orig-linux-2.6.16.29/include/linux/oprofile.h 2006-09-12 19:02:10.000000000 +0100
-+++ ./include/linux/oprofile.h 2006-09-19 14:06:05.000000000 +0100
+--- ../orig-linux-2.6.16.29/include/linux/oprofile.h 2006-11-06 14:46:42.000000000 -0800
++++ ./include/linux/oprofile.h 2006-11-06 14:47:55.000000000 -0800
@@ -16,6 +16,8 @@
#include <linux/types.h>
#include <linux/spinlock.h>
diff --git a/tools/Makefile b/tools/Makefile
index f1f25f19bf..fa9d63da16 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -18,6 +18,7 @@ SUBDIRS-$(VTPM_TOOLS) += vtpm
SUBDIRS-y += xenstat
SUBDIRS-y += libaio
SUBDIRS-y += blktap
+SUBDIRS-y += libfsimage
# These don't cross-compile
ifeq ($(XEN_COMPILE_ARCH),$(XEN_TARGET_ARCH))
diff --git a/tools/blktap/drivers/blktapctrl.c b/tools/blktap/drivers/blktapctrl.c
index 0b00bc4bfd..ae76f60cad 100644
--- a/tools/blktap/drivers/blktapctrl.c
+++ b/tools/blktap/drivers/blktapctrl.c
@@ -607,9 +607,11 @@ int main(int argc, char *argv[])
struct xs_handle *h;
struct pollfd pfd[NUM_POLL_FDS];
pid_t process;
+ char buf[128];
__init_blkif();
- openlog("BLKTAPCTRL", LOG_CONS|LOG_ODELAY, LOG_DAEMON);
+ snprintf(buf, sizeof(buf), "BLKTAPCTRL[%d]", getpid());
+ openlog(buf, LOG_CONS|LOG_ODELAY, LOG_DAEMON);
daemon(0,0);
print_drivers();
diff --git a/tools/blktap/drivers/tapdisk.c b/tools/blktap/drivers/tapdisk.c
index 859687d8b3..0c9b623abe 100644
--- a/tools/blktap/drivers/tapdisk.c
+++ b/tools/blktap/drivers/tapdisk.c
@@ -381,7 +381,6 @@ static inline int write_rsp_to_ring(struct td_state *s, blkif_response_t *rsp)
rsp_d = RING_GET_RESPONSE(&info->fe_ring, info->fe_ring.rsp_prod_pvt);
memcpy(rsp_d, rsp, sizeof(blkif_response_t));
- wmb();
info->fe_ring.rsp_prod_pvt++;
return 0;
@@ -562,12 +561,14 @@ int main(int argc, char *argv[])
fd_list_entry_t *ptr;
struct tap_disk *drv;
struct td_state *s;
+ char openlogbuf[128];
if (argc != 3) usage();
daemonize();
- openlog("TAPDISK", LOG_CONS|LOG_ODELAY, LOG_DAEMON);
+ 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);
diff --git a/tools/blktap/drivers/tapdisk.h b/tools/blktap/drivers/tapdisk.h
index 238350016b..2455b5282e 100644
--- a/tools/blktap/drivers/tapdisk.h
+++ b/tools/blktap/drivers/tapdisk.h
@@ -61,7 +61,6 @@
/* Things disks need to know about, these should probably be in a higher-level
* header. */
-#define MAX_REQUESTS 64
#define MAX_SEGMENTS_PER_REQ 11
#define SECTOR_SHIFT 9
#define DEFAULT_SECTOR_SIZE 512
diff --git a/tools/blktap/lib/blktaplib.h b/tools/blktap/lib/blktaplib.h
index 456a368786..456497a608 100644
--- a/tools/blktap/lib/blktaplib.h
+++ b/tools/blktap/lib/blktaplib.h
@@ -41,7 +41,7 @@
#include <sys/types.h>
#include <unistd.h>
-#define BLK_RING_SIZE __RING_SIZE((blkif_sring_t *)0, getpagesize())
+#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
@@ -74,10 +74,10 @@ static inline int BLKTAP_MODE_VALID(unsigned long arg)
( arg == BLKTAP_MODE_INTERPOSE ) );
}
-#define MAX_REQUESTS 64
+#define MAX_REQUESTS BLK_RING_SIZE
#define BLKTAP_IOCTL_KICK 1
-#define MAX_PENDING_REQS 64
+#define MAX_PENDING_REQS BLK_RING_SIZE
#define BLKTAP_DEV_DIR "/dev/xen"
#define BLKTAP_DEV_NAME "blktap"
#define BLKTAP_DEV_MINOR 0
@@ -199,7 +199,6 @@ int xs_fire_next_watch(struct xs_handle *h);
/* Abitrary values, must match the underlying driver... */
-#define MAX_PENDING_REQS 64
#define MAX_TAP_DEV 100
/* Accessing attached data page mappings */
diff --git a/tools/console/Makefile b/tools/console/Makefile
index 55e08e8f2d..f33204cfaf 100644
--- a/tools/console/Makefile
+++ b/tools/console/Makefile
@@ -5,7 +5,7 @@ include $(XEN_ROOT)/tools/Rules.mk
DAEMON_INSTALL_DIR = /usr/sbin
CLIENT_INSTALL_DIR = /usr/$(LIBDIR)/xen/bin
-CFLAGS += -Werror -g
+CFLAGS += -Werror
CFLAGS += -I $(XEN_LIBXC)
CFLAGS += -I $(XEN_XENSTORE)
diff --git a/tools/examples/blktap b/tools/examples/blktap
index ba9f4ee52f..bc25b48be2 100644
--- a/tools/examples/blktap
+++ b/tools/examples/blktap
@@ -4,12 +4,26 @@
dir=$(dirname "$0")
. "$dir/xen-hotplug-common.sh"
+. "$dir/block-common.sh"
findCommand "$@"
-if [ "$command" == 'add' ]
+t=$(xenstore_read_default "$XENBUS_PATH/type" 'MISSING')
+if [ -n "$t" ]
then
- success
+ p=$(xenstore_read "$XENBUS_PATH/params")
+ # if we have a ':', chew from head including :
+ if echo $p | grep -q \:
+ then
+ p=${p#*:}
+ fi
+fi
+file=$(readlink -f "$p") || ebusy "$p does not exist."
+
+if [ "$command" = 'add' ]
+then
+ [ -e "$file" ] || { ebusy $file does not exist; }
+ success
fi
exit 0
diff --git a/tools/examples/block b/tools/examples/block
index 93d7550e06..8ec73231bf 100644
--- a/tools/examples/block
+++ b/tools/examples/block
@@ -68,7 +68,7 @@ check_sharing()
local devmm=$(device_major_minor "$dev")
local file
- if [ "$mode" == 'w' ]
+ if [ "$mode" = 'w' ]
then
toskip="^$"
else
@@ -81,7 +81,7 @@ check_sharing()
then
local d=$(device_major_minor "$file")
- if [ "$d" == "$devmm" ]
+ if [ "$d" = "$devmm" ]
then
echo 'local'
return
@@ -96,9 +96,9 @@ check_sharing()
do
d=$(xenstore_read_default "$base_path/$dom/$dev/physical-device" "")
- if [ "$d" == "$devmm" ]
+ if [ "$d" = "$devmm" ]
then
- if [ "$mode" == 'w' ]
+ if [ "$mode" = 'w' ]
then
if ! same_vm $dom
then
@@ -109,7 +109,7 @@ check_sharing()
local m=$(xenstore_read "$base_path/$dom/$dev/mode")
m=$(canonicalise_mode "$m")
- if [ "$m" == 'w' ]
+ if [ "$m" = 'w' ]
then
if ! same_vm $dom
then
@@ -138,7 +138,7 @@ same_vm()
local othervm=$(xenstore_read_default "/local/domain/$otherdom/vm" \
"$FRONTEND_UUID")
- [ "$FRONTEND_UUID" == "$othervm" ]
+ [ "$FRONTEND_UUID" = "$othervm" ]
}
@@ -153,7 +153,7 @@ check_device_sharing()
local mode=$(canonicalise_mode "$2")
local result
- if [ "$mode" == '!' ]
+ if [ "x$mode" = 'x!' ]
then
return 0
fi
@@ -202,7 +202,7 @@ do_ebusy()
local mode="$2"
local result="$3"
- if [ "$result" == 'guest' ]
+ if [ "$result" = 'guest' ]
then
dom='a guest '
when='now'
@@ -211,7 +211,7 @@ do_ebusy()
when='by a guest'
fi
- if [ "$mode" == 'w' ]
+ if [ "$mode" = 'w' ]
then
m1=''
m2=''
@@ -266,7 +266,7 @@ case "$command" in
claim_lock "block"
- if [ "$mode" == 'w' ] && ! stat "$file" -c %A | grep -q w
+ if [ "$mode" = 'w' ] && ! stat "$file" -c %A | grep -q w
then
release_lock "block"
ebusy \
@@ -287,7 +287,7 @@ mount it read-write in a guest domain."
if [ "$f" ]
then
# $dev is in use. Check sharing.
- if [ "$mode" == '!' ]
+ if [ "x$mode" = 'x!' ]
then
continue
fi
@@ -307,7 +307,7 @@ mount it read-write in a guest domain."
do
d=$(xenstore_read_default \
"$XENBUS_BASE_PATH/$dom/$domdev/node" "")
- if [ "$d" == "$dev" ]
+ if [ "$d" = "$dev" ]
then
f=$(xenstore_read "$XENBUS_BASE_PATH/$dom/$domdev/params")
found=1
@@ -347,7 +347,7 @@ mount it read-write in a guest domain."
f=$(readlink -f "$f" || echo $(dirname "$file")/$(basename "$f"))
- if [ "$f" == "$file" ]
+ if [ "$f" = "$file" ]
then
check_file_sharing "$file" "$dev" "$mode"
fi
@@ -355,14 +355,14 @@ mount it read-write in a guest domain."
# $dev is not in use, so we'll remember it for use later; we want
# to finish the sharing check first.
- if [ "$loopdev" == '' ]
+ if [ "$loopdev" = '' ]
then
loopdev="$dev"
fi
fi
done
- if [ "$loopdev" == '' ]
+ if [ "$loopdev" = '' ]
then
fatal 'Failed to find an unused loop device'
fi
diff --git a/tools/examples/external-device-migrate b/tools/examples/external-device-migrate
index 7d58454525..423c513930 100644
--- a/tools/examples/external-device-migrate
+++ b/tools/examples/external-device-migrate
@@ -55,41 +55,27 @@ function evaluate_params()
{
local step host domname typ recover filename func stype
stype=""
- while [ 1 ]; do
- if [ "$1" == "-step" ]; then
- shift
- step=$1
- elif [ "$1" == "-host" ]; then
- shift
- host=$1
- elif [ "$1" == "-domname" ]; then
- shift
- domname=$1
- elif [ "$1" == "-type" ]; then
- shift
- typ=$1
- elif [ "$1" == "-subtype" ]; then
- shift
- stype="_$1"
- elif [ "$1" == "-recover" ]; then
- recover=1
- elif [ "$1" == "-help" ]; then
- ext_dev_migrate_usage
- exit
- else
- break
- fi
- shift
+ while [ $# -ge 1 ]; do
+ case "$1" in
+ -step) step=$2; shift 2;;
+ -host) host=$2; shift 2;;
+ -domname) domname=$2; shift 2;;
+ -type) type=$2; shift 2;;
+ -subtype) subtype=$2; shift 2;;
+ -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)"
- echo ""
- echo "$0 -help for usage."
- exit
+ 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"
@@ -99,7 +85,7 @@ function evaluate_params()
fi
. "$filename"
- if [ "$recover" == "1" ]; then
+ if [ "$recover" = "1" ]; then
func="$typ"_recover
eval $func $host $domname $step $*
else
@@ -108,4 +94,4 @@ function evaluate_params()
fi
}
-evaluate_params $*
+evaluate_params "$@"
diff --git a/tools/examples/vif-bridge b/tools/examples/vif-bridge
index c9d2b49316..7008210579 100755
--- a/tools/examples/vif-bridge
+++ b/tools/examples/vif-bridge
@@ -61,7 +61,7 @@ esac
handle_iptable
log debug "Successful vif-bridge $command for $vif, bridge $bridge."
-if [ "$command" == "online" ]
+if [ "$command" = "online" ]
then
success
fi
diff --git a/tools/examples/vif-nat b/tools/examples/vif-nat
index 0f1cb1c89f..29611654eb 100644
--- a/tools/examples/vif-nat
+++ b/tools/examples/vif-nat
@@ -72,7 +72,7 @@ dotted_quad()
}
-if [ "$ip" == "" ]
+if [ "$ip" = "" ]
then
ip=$(ip_from_dom)
fi
@@ -152,7 +152,7 @@ esac
handle_iptable
log debug "Successful vif-nat $command for $vif."
-if [ "$command" == "online" ]
+if [ "$command" = "online" ]
then
success
fi
diff --git a/tools/examples/vif-route b/tools/examples/vif-route
index 70ac49fc51..8d0fb8d76c 100755
--- a/tools/examples/vif-route
+++ b/tools/examples/vif-route
@@ -50,7 +50,7 @@ fi
handle_iptable
log debug "Successful vif-route $command for $vif."
-if [ "$command" == "online" ]
+if [ "$command" = "online" ]
then
success
fi
diff --git a/tools/examples/xend-config.sxp b/tools/examples/xend-config.sxp
index 294d8e08de..514e5c3f2b 100644
--- a/tools/examples/xend-config.sxp
+++ b/tools/examples/xend-config.sxp
@@ -51,7 +51,7 @@
# regular expressions will be accepted.
#
# For example:
-# (xend-relocation-hosts-allow '^localhost$ ^.*\.example\.org$')
+# (xend-relocation-hosts-allow '^localhost$ ^.*\\.example\\.org$')
#
#(xend-relocation-hosts-allow '')
(xend-relocation-hosts-allow '^localhost$ ^localhost\\.localdomain$')
diff --git a/tools/examples/xmexample.hvm b/tools/examples/xmexample.hvm
index 20b58af44b..5f10a76fb6 100644
--- a/tools/examples/xmexample.hvm
+++ b/tools/examples/xmexample.hvm
@@ -48,9 +48,6 @@ name = "ExampleHVMDomain"
# enable/disable HVM guest ACPI, default=0 (disabled)
#acpi=0
-# enable/disable HVM guest APIC, default=0 (disabled)
-#apic=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
diff --git a/tools/firmware/Makefile b/tools/firmware/Makefile
index a024e1249e..eb734d987c 100644
--- a/tools/firmware/Makefile
+++ b/tools/firmware/Makefile
@@ -9,7 +9,6 @@ INST_DIR := $(DESTDIR)/usr/lib/xen/boot
SUBDIRS :=
SUBDIRS += rombios
SUBDIRS += vgabios
-SUBDIRS += acpi
SUBDIRS += vmxassist
SUBDIRS += hvmloader
diff --git a/tools/firmware/acpi/Makefile b/tools/firmware/acpi/Makefile
deleted file mode 100644
index 3b4bb99b49..0000000000
--- a/tools/firmware/acpi/Makefile
+++ /dev/null
@@ -1,68 +0,0 @@
-#/*
-# * 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/Rules.mk
-
-# Compiler flag
-HOSTCFLAGS += -I. -I../../libxc
-
-# TARGET
-C_SRC=$(shell ls *.c)
-H_SRC=$(shell ls *.h)
-ACPI_GEN=acpigen
-ACPI_BIN=acpi.bin
-
-IASL_VER=acpica-unix-20050513
-IASL_URL=http://developer.intel.com/technology/iapc/acpi/downloads/$(IASL_VER).tar.gz
-
-vpath iasl $(PATH)
-all:$(ACPI_BIN)
-
-acpi_dsdt.c:acpi_dsdt.asl
- $(MAKE) iasl
- iasl -tc acpi_dsdt.asl
- mv acpi_dsdt.hex acpi_dsdt.c
- echo "int DsdtLen=sizeof(AmlCode);" >> acpi_dsdt.c
- rm *.aml
-# iasl -oa -tc acpi_dsdt.asl
-
-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://developer.intel.com/technology/iapc/acpi/downloads.htm"
- @echo
- wget $(IASL_URL)
- tar xzf $(IASL_VER).tar.gz
- make -C $(IASL_VER)/compiler
- $(INSTALL_PROG) $(IASL_VER)/compiler/iasl /usr/bin/iasl
-
-$(ACPI_GEN):$(C_SRC) $(H_SRC) acpi_dsdt.c
- $(HOSTCC) -o $(ACPI_GEN) $(HOSTCFLAGS) $(shell ls *.c)
-
-$(ACPI_BIN):$(ACPI_GEN)
- ./$(ACPI_GEN) $(ACPI_BIN)
-
-clean:
- rm -rf *.o $(ACPI_GEN) $(ACPI_BIN) $(IASL_VER)
- rm -rf $(IASL_VER).tar.gz
-
-install: all
diff --git a/tools/firmware/acpi/acpi2_0.h b/tools/firmware/acpi/acpi2_0.h
deleted file mode 100644
index 709511df58..0000000000
--- a/tools/firmware/acpi/acpi2_0.h
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * 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_
-
-typedef unsigned char uint8_t;
-typedef signed char int8_t;
-typedef unsigned short uint16_t;
-typedef signed short int16_t;
-typedef unsigned int uint32_t;
-typedef signed int int32_t;
-#ifdef __i386__
-typedef unsigned long long uint64_t;
-typedef signed long long int64_t;
-#else
-typedef unsigned long uint64_t;
-typedef signed long int64_t;
-#endif
-
-#include <xen/xen.h>
-
-#pragma pack (1)
-
-//
-// common ACPI header.
-//
-
-typedef struct {
- uint32_t Signature;
- uint32_t Length;
- uint8_t Revision;
- uint8_t Checksum;
- uint8_t OemId[6];
- uint64_t OemTableId;
- uint32_t OemRevision;
- uint32_t CreatorId;
- uint32_t CreatorRevision;
-} ACPI_TABLE_HEADER;
-
-
-#define ACPI_OEM_ID {'I','N','T','E','L',' '}
-#define ACPI_OEM_TABLE_ID 0x544244 // "TBD"
-#define ACPI_OEM_REVISION 0x00000002
-#define ACPI_CREATOR_ID 0x00 // TBD
-#define ACPI_CREATOR_REVISION 0x00000002
-
-//
-// ACPI 2.0 Generic Address Space definition
-//
-typedef struct {
- uint8_t AddressSpaceId;
- uint8_t RegisterBitWidth;
- uint8_t RegisterBitOffset;
- uint8_t Reserved;
- uint64_t Address;
-} ACPI_GENERIC_ADDRESS_STRUCTURE;
-
-//
-// 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
-//
-typedef struct {
- uint64_t Signature;
- uint8_t Checksum;
- uint8_t OemId[6];
- uint8_t Reserved;
- uint32_t RsdtAddress;
-} ACPI_1_0_RSDP;
-
-
-//
-// Root System Description Pointer Structure
-//
-typedef struct {
- uint64_t Signature;
- uint8_t Checksum;
- uint8_t OemId[6];
- uint8_t Revision;
- uint32_t RsdtAddress;
- uint32_t Length;
- uint64_t XsdtAddress;
- uint8_t ExtendedChecksum;
- uint8_t Reserved[3];
-} ACPI_2_0_RSDP;
-
-
-//
-// The maximum number of entrys in RSDT or XSDT
-//
-#define ACPI_MAX_NUM_TABLES 2
-
-//
-// Root System Description Table (RSDT)
-//
-
-typedef struct {
- ACPI_TABLE_HEADER Header;
- uint32_t Entry[ACPI_MAX_NUM_TABLES];
-}ACPI_2_0_RSDT;
-
-//
-// RSDT Revision (as defined in ACPI 2.0 spec.)
-//
-
-#define ACPI_2_0_RSDT_REVISION 0x01
-
-//
-// Extended System Description Table (XSDT)
-//
-
-typedef struct _ACPI_2_0_XSDT{
- ACPI_TABLE_HEADER Header;
- uint64_t Entry[ACPI_MAX_NUM_TABLES];
-}ACPI_2_0_XSDT;
-#define ACPI_2_0_XSDT_REVISION 0x01
-
-//
-// Fixed ACPI Description Table Structure (FADT)
-//
-
-typedef struct {
- ACPI_TABLE_HEADER Header;
- uint32_t FirmwareCtrl;
- uint32_t Dsdt;
- uint8_t Reserved0;
- uint8_t PreferredPmProfile;
- uint16_t SciInt;
- uint32_t SmiCmd;
- uint8_t AcpiEnable;
- uint8_t AcpiDisable;
- uint8_t S4BiosReq;
- uint8_t PstateCnt;
- uint32_t Pm1aEvtBlk;
- uint32_t Pm1bEvtBlk;
- uint32_t Pm1aCntBlk;
- uint32_t Pm1bCntBlk;
- uint32_t Pm2CntBlk;
- uint32_t PmTmrBlk;
- uint32_t Gpe0Blk;
- uint32_t Gpe1Blk;
- uint8_t Pm1EvtLen;
- uint8_t Pm1CntLen;
- uint8_t Pm2CntLen;
- uint8_t PmTmrLen;
- uint8_t Gpe0BlkLen;
- uint8_t Gpe1BlkLen;
- uint8_t Gpe1Base;
- uint8_t CstCnt;
- uint16_t PLvl2Lat;
- uint16_t PLvl3Lat;
- uint16_t FlushSize;
- uint16_t FlushStride;
- uint8_t DutyOffset;
- uint8_t DutyWidth;
- uint8_t DayAlrm;
- uint8_t MonAlrm;
- uint8_t Century;
- uint16_t IaPcBootArch;
- uint8_t Reserved1;
- uint32_t Flags;
- ACPI_GENERIC_ADDRESS_STRUCTURE ResetReg;
- uint8_t ResetValue;
- uint8_t Reserved2[3];
- uint64_t XFirmwareCtrl;
- uint64_t XDsdt;
- ACPI_GENERIC_ADDRESS_STRUCTURE XPm1aEvtBlk;
- ACPI_GENERIC_ADDRESS_STRUCTURE XPm1bEvtBlk;
- ACPI_GENERIC_ADDRESS_STRUCTURE XPm1aCntBlk;
- ACPI_GENERIC_ADDRESS_STRUCTURE XPm1bCntBlk;
- ACPI_GENERIC_ADDRESS_STRUCTURE XPm2CntBlk;
- ACPI_GENERIC_ADDRESS_STRUCTURE XPmTmrBlk;
- ACPI_GENERIC_ADDRESS_STRUCTURE XGpe0Blk;
- ACPI_GENERIC_ADDRESS_STRUCTURE XGpe1Blk;
-} ACPI_2_0_FADT;
-#define ACPI_2_0_FADT_REVISION 0x03
-
-//
-// 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)
-//
-typedef struct {
- uint32_t Signature;
- uint32_t Length;
- uint32_t HardwareSignature;
- uint32_t FirmwareWakingVector;
- uint32_t GlobalLock;
- uint32_t Flags;
- uint64_t XFirmwareWakingVector;
- uint8_t Version;
- uint8_t Reserved[31];
-} ACPI_2_0_FACS;
-
-#define ACPI_2_0_FACS_VERSION 0x01
-
-//
-// Multiple APIC Description Table header definition (MADT)
-//
-typedef struct {
- ACPI_TABLE_HEADER Header;
- uint32_t LocalApicAddress;
- uint32_t Flags;
-} ACPI_2_0_MADT;
-
-#define ACPI_2_0_MADT_REVISION 0x01
-
-//
-// 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
-//
-
-typedef struct {
- uint8_t Type;
- uint8_t Length;
- uint8_t AcpiProcessorId;
- uint8_t ApicId;
- uint32_t Flags;
-} ACPI_LOCAL_APIC_STRUCTURE;
-
-//
-// Local APIC Flags. All other bits are reserved and must be 0.
-//
-
-#define ACPI_LOCAL_APIC_ENABLED (1 << 0)
-
-//
-// IO APIC Structure
-//
-
-typedef struct {
- uint8_t Type;
- uint8_t Length;
- uint8_t IoApicId;
- uint8_t Reserved;
- uint32_t IoApicAddress;
- uint32_t GlobalSystemInterruptBase;
-} ACPI_IO_APIC_STRUCTURE;
-
-// Tabel Signature
-#define ACPI_2_0_RSDP_SIGNATURE 0x2052545020445352LL // "RSD PTR "
-
-#define ACPI_DIFFERENTIATED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE 0x54445344 //"DSDT"
-
-#define ACPI_2_0_FACS_SIGNATURE 0x53434146 // "FACS"
-
-#define ACPI_2_0_FADT_SIGNATURE 0x50434146 // "FADT"
-
-#define ACPI_MULTIPLE_APIC_DESCRIPTION_TABLE_SIGNATURE 0x43495041 // "APIC"
-
-#define ACPI_2_0_RSDT_SIGNATURE 0x54445352 // "RSDT"
-
-#define ACPI_2_0_XSDT_SIGNATURE 0x54445358 // "XSDT"
-
-#pragma pack ()
-
-// The physical that acpi table reside in the guest BIOS
-//#define ACPI_PHYSICAL_ADDRESS 0xE2000
-#define ACPI_PHYSICAL_ADDRESS 0xEA000
-#define ACPI_TABLE_SIZE (4*1024) //Currently 4K is enough
-
-void
-AcpiBuildTable(uint8_t* buf);
-
-#endif
diff --git a/tools/firmware/acpi/acpi_build.c b/tools/firmware/acpi/acpi_build.c
deleted file mode 100644
index 7c58265309..0000000000
--- a/tools/firmware/acpi/acpi_build.c
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * 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.
- *
- */
-
-#include "acpi2_0.h"
-#include "acpi_madt.h"
-
-extern ACPI_2_0_RSDP Rsdp;
-extern ACPI_2_0_RSDT Rsdt;
-extern ACPI_2_0_XSDT Xsdt;
-extern ACPI_2_0_FADT Fadt;
-extern ACPI_MULTIPLE_APIC_DESCRIPTION_TABLE Madt;
-extern ACPI_2_0_FACS Facs;
-extern unsigned char *AmlCode;
-extern int DsdtLen;
-
-
-typedef struct _ACPI_TABLE_ALL{
- ACPI_2_0_RSDP *Rsdp;
- ACPI_2_0_RSDT *Rsdt;
- ACPI_2_0_XSDT *Xsdt;
- ACPI_2_0_FADT *Fadt;
- ACPI_MULTIPLE_APIC_DESCRIPTION_TABLE *Madt;
- ACPI_2_0_FACS *Facs;
- unsigned char* Dsdt;
- uint32_t RsdpOffset;
- uint32_t RsdtOffset;
- uint32_t XsdtOffset;
- uint32_t FadtOffset;
- uint32_t MadtOffset;
- uint32_t FacsOffset;
- uint32_t DsdtOffset;
-}ACPI_TABLE_ALL;
-
-static
-void
-MemCopy(void* src, void* dst, int len){
-
- uint8_t* src0=src;
- uint8_t* dst0=dst;
-
- while(len--){
- *(dst0++)=*(src0++);
- }
-}
-
-static
-void
-SetCheckSum(
- void* Table,
- uint32_t ChecksumOffset,
- uint32_t Length
-)
-/*
- * Routine Description:
- * Calculate Checksum and store the result in the checksum
- * filed of the table
- *
- * INPUT:
- * Table: Start pointer of table
- * ChecksumOffset: Offset of checksum field in the table
- * Length: Length of Table
- */
-{
- uint8_t Sum = 0;
- uint8_t *Ptr;
-
- Ptr=Table;
- Ptr[ChecksumOffset]=0;
- while (Length--) {
- Sum = (uint8_t)(Sum + (*Ptr++));
- }
-
- Ptr = Table;
- Ptr[ChecksumOffset] = (uint8_t) (0xff - Sum + 1);
-}
-
-//
-// FIELD_OFFSET - returns the byte offset to a field within a structure
-//
-#define FIELD_OFFSET(TYPE,Field) ((uint32_t)(&(((TYPE *) 0)->Field)))
-
-static
-void
-UpdateTable(
- ACPI_TABLE_ALL *table
-)
-/*
- * Update the ACPI table:
- * fill in the actuall physical address of RSDT, XSDT, FADT, MADT, FACS
- * Caculate the checksum
- */
-{
- // RSDP Update
- table->Rsdp->RsdtAddress = (uint32_t)(ACPI_PHYSICAL_ADDRESS+
- table->RsdtOffset);
- table->Rsdp->XsdtAddress = (uint64_t)(ACPI_PHYSICAL_ADDRESS+
- table->XsdtOffset);
- SetCheckSum(table->Rsdp,
- FIELD_OFFSET(ACPI_1_0_RSDP, Checksum),
- sizeof(ACPI_1_0_RSDP)
- );
- SetCheckSum(table->Rsdp,
- FIELD_OFFSET(ACPI_2_0_RSDP,
- ExtendedChecksum),
- sizeof(ACPI_2_0_RSDP)
- );
-
-
- //RSDT Update
- table->Rsdt->Entry[0] = (uint32_t)(ACPI_PHYSICAL_ADDRESS +
- table->FadtOffset);
- table->Rsdt->Entry[1] = (uint32_t)(ACPI_PHYSICAL_ADDRESS +
- table->MadtOffset);
- table->Rsdt->Header.Length = sizeof (ACPI_TABLE_HEADER) +
- 2*sizeof(uint32_t);
- SetCheckSum(table->Rsdt,
- FIELD_OFFSET(ACPI_TABLE_HEADER, Checksum),
- table->Rsdt->Header.Length
- );
-
- //XSDT Update
- table->Xsdt->Entry[0] = (uint64_t)(ACPI_PHYSICAL_ADDRESS +
- table->FadtOffset);
- table->Xsdt->Entry[1] = (uint64_t)(ACPI_PHYSICAL_ADDRESS +
- table->MadtOffset);
- table->Xsdt->Header.Length = sizeof (ACPI_TABLE_HEADER) +
- 2*sizeof(uint64_t);
- SetCheckSum(table->Xsdt,
- FIELD_OFFSET(ACPI_TABLE_HEADER, Checksum),
- table->Xsdt->Header.Length
- );
-
- // FADT Update
- table->Fadt->Dsdt = (uint32_t)(ACPI_PHYSICAL_ADDRESS +
- table->DsdtOffset);
- table->Fadt->XDsdt = (uint64_t)(ACPI_PHYSICAL_ADDRESS +
- table->DsdtOffset);
- table->Fadt->FirmwareCtrl = (uint32_t)(ACPI_PHYSICAL_ADDRESS +
- table->FacsOffset);
- table->Fadt->XFirmwareCtrl = (uint64_t)(ACPI_PHYSICAL_ADDRESS +
- table->FacsOffset);
- SetCheckSum(table->Fadt,
- FIELD_OFFSET(ACPI_TABLE_HEADER, Checksum),
- sizeof(ACPI_2_0_FADT)
- );
-
- // MADT update
- SetCheckSum(table->Madt,
- FIELD_OFFSET(ACPI_TABLE_HEADER, Checksum),
- sizeof(ACPI_MULTIPLE_APIC_DESCRIPTION_TABLE)
- );
-}
-
-void
-AcpiBuildTable(uint8_t* buf)
-/*
- * Copy all the ACPI table to buffer
- * Buffer Layout:
- * FACS
- * RSDP
- * RSDT
- * XSDT
- * FADT
- * MADT
- * DSDT
- *
- */
-{
- ACPI_TABLE_ALL table;
- int offset=0;
-
- // FACS: should be 64-bit alignment
- // so it is put at the start of buffer
- // as the buffer is 64 bit alignment
- table.FacsOffset = offset;
- table.Facs = (ACPI_2_0_FACS*)(&buf[offset]);
- MemCopy(&Facs, table.Facs, sizeof(ACPI_2_0_FACS));
- offset += sizeof(ACPI_2_0_FACS);
-
- // RSDP
- table.RsdpOffset = offset;
- table.Rsdp = (ACPI_2_0_RSDP*)(&buf[offset]);
- MemCopy(&Rsdp, table.Rsdp, sizeof(ACPI_2_0_RSDP));
- offset+=sizeof(ACPI_2_0_RSDP);
-
- // RSDT
- table.RsdtOffset = offset;
- table.Rsdt = (ACPI_2_0_RSDT*)(&buf[offset]);
- MemCopy(&Rsdt, table.Rsdt, sizeof(ACPI_2_0_RSDT));
- offset+=sizeof(ACPI_2_0_RSDT);
-
- // XSDT
- table.XsdtOffset = offset;
- table.Xsdt = (ACPI_2_0_XSDT*)(&buf[offset]);
- MemCopy(&Xsdt, table.Xsdt, sizeof(ACPI_2_0_XSDT));
- offset+=sizeof(ACPI_2_0_XSDT);
-
- // FADT
- table.FadtOffset = offset;
- table.Fadt = (ACPI_2_0_FADT*)(&buf[offset]);
- MemCopy(&Fadt, table.Fadt, sizeof(ACPI_2_0_FADT));
- offset+=sizeof(ACPI_2_0_FADT);
-
- // MADT
- table.MadtOffset = offset;
- table.Madt = (ACPI_MULTIPLE_APIC_DESCRIPTION_TABLE*)(&buf[offset]);
- MemCopy(&Madt, table.Madt, sizeof(ACPI_MULTIPLE_APIC_DESCRIPTION_TABLE));
- offset+=sizeof(ACPI_MULTIPLE_APIC_DESCRIPTION_TABLE);
-
- // DSDT
- table.DsdtOffset = offset;
- table.Dsdt = (unsigned char*)(&buf[offset]);
- MemCopy(&AmlCode, table.Dsdt, DsdtLen);
- offset+=DsdtLen;
-
- UpdateTable(&table);
-}
diff --git a/tools/firmware/acpi/acpi_facs.c b/tools/firmware/acpi/acpi_facs.c
deleted file mode 100644
index 12947d0fed..0000000000
--- a/tools/firmware/acpi/acpi_facs.c
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * 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.
- *
- */
-#include "acpi2_0.h"
-#include "acpi_facs.h"
-
-//
-// Firmware ACPI Control Structure
-//
-
-ACPI_2_0_FACS Facs = {
- ACPI_2_0_FACS_SIGNATURE,
- sizeof (ACPI_2_0_FACS),
-
- //
- // Hardware Signature
- //
- 0x00000000,
-
- ACPI_FIRMWARE_WAKING_VECTOR,
- ACPI_GLOBAL_LOCK,
- ACPI_FIRMWARE_CONTROL_STRUCTURE_FLAGS,
- ACPI_X_FIRMWARE_WAKING_VECTOR,
- ACPI_2_0_FACS_VERSION,
- {
- 0x00, // Reserved Fields
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- }
-};
diff --git a/tools/firmware/acpi/acpi_facs.h b/tools/firmware/acpi/acpi_facs.h
deleted file mode 100644
index e8c55a30ea..0000000000
--- a/tools/firmware/acpi/acpi_facs.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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 _FACS_H
-#define _FACS_H
-
-//
-// FACS Definitions
-//
-
-#define ACPI_FIRMWARE_WAKING_VECTOR 0x00000000
-#define ACPI_GLOBAL_LOCK 0x00000000
-
-#define ACPI_FIRMWARE_CONTROL_STRUCTURE_FLAGS 0x00000000
-
-#define ACPI_X_FIRMWARE_WAKING_VECTOR 0x0000000000000000
-
-#endif
diff --git a/tools/firmware/acpi/acpi_fadt.c b/tools/firmware/acpi/acpi_fadt.c
deleted file mode 100644
index 39b970c203..0000000000
--- a/tools/firmware/acpi/acpi_fadt.c
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * 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.
- *
- */
-
-#include "acpi2_0.h"
-#include "acpi_fadt.h"
-
-//
-// Fixed ACPI Description Table
-//
-
-ACPI_2_0_FADT Fadt = {
- {
- ACPI_2_0_FADT_SIGNATURE,
- sizeof (ACPI_2_0_FADT),
- ACPI_2_0_FADT_REVISION,
- 0x00,// Checksum will be updated later
- ACPI_OEM_ID, // OEM ID
- ACPI_OEM_TABLE_ID, // OEM Table ID
- ACPI_OEM_REVISION, // OEM Revision
- ACPI_CREATOR_ID, // Creator ID
- ACPI_CREATOR_REVISION, // Creator Revision
- },
- //
- // These addresses will be updated later
- //
- 0x00000000, // Physical Address (0~4G) of the FACS
- 0x00000000, // Physical Address (0~4G) of the DSDT
-
- 0x00,
- ACPI_PREFERRED_PM_PROFILE, // Enterprise
- ACPI_SCI_INT, // IRQ 9
- ACPI_SMI_CMD,
- ACPI_ACPI_ENABLE,
- ACPI_ACPI_DISABLE,
- ACPI_S4_BIOS_REQ, // zero. not supported
- ACPI_PSTATE_CNT, // not supported
-
- ACPI_PM1A_EVT_BLK_ADDRESS, // required
- ACPI_PM1B_EVT_BLK_ADDRESS, // not supported
- ACPI_PM1A_CNT_BLK_ADDRESS, // required
- ACPI_PM1B_CNT_BLK_ADDRESS, // not supported
- ACPI_PM2_CNT_BLK_ADDRESS, // not supported
- ACPI_PM_TMR_BLK_ADDRESS, // required
- ACPI_GPE0_BLK_ADDRESS, // not supported
- ACPI_GPE1_BLK_ADDRESS, // not supported
- ACPI_PM1_EVT_LEN,
- ACPI_PM1_CNT_LEN,
- ACPI_PM2_CNT_LEN,
- ACPI_PM_TMR_LEN,
- ACPI_GPE0_BLK_LEN,
- ACPI_GPE1_BLK_LEN,
- ACPI_GPE1_BASE,
-
- ACPI_CST_CNT,
- ACPI_P_LVL2_LAT, // >100, not support C2 state
- ACPI_P_LVL3_LAT, // >1000, not support C3 state
- ACPI_FLUSH_SIZE, // not support
- ACPI_FLUSH_STRIDE, // not support
- ACPI_DUTY_OFFSET, // not support
- ACPI_DUTY_WIDTH, // not support
- ACPI_DAY_ALRM, // not support
- ACPI_MON_ALRM, // not support
- ACPI_CENTURY, // not support
- ACPI_IAPC_BOOT_ARCH,
- 0x00,
- ACPI_FIXED_FEATURE_FLAGS,
-
- //
- // Reset Register Block
- //
- { ACPI_RESET_REG_ADDRESS_SPACE_ID,
- ACPI_RESET_REG_BIT_WIDTH,
- ACPI_RESET_REG_BIT_OFFSET,
- 0x00,
- ACPI_RESET_REG_ADDRESS,
- },
-
- ACPI_RESET_VALUE,
- {
- 0x00,
- 0x00,
- 0x00,
- },
- //
- // These addresses will be updated later
- //
- 0x0000000000000000, // X_FIRMWARE_CTRL: 64bit physical address of the FACS.
- 0x0000000000000000, // X_DSDT: 64bit physical address of the DSDT.
-
- //
- // PM1a Event Register Block
- //
- {
- ACPI_PM1A_EVT_BLK_ADDRESS_SPACE_ID,
- ACPI_PM1A_EVT_BLK_BIT_WIDTH,
- ACPI_PM1A_EVT_BLK_BIT_OFFSET,
- 0x00,
- ACPI_PM1A_EVT_BLK_ADDRESS,
- },
-
- //
- // PM1b Event Register Block
- //
- {
- ACPI_PM1B_EVT_BLK_ADDRESS_SPACE_ID, // not support
- ACPI_PM1B_EVT_BLK_BIT_WIDTH,
- ACPI_PM1B_EVT_BLK_BIT_OFFSET,
- 0x00,
- ACPI_PM1B_EVT_BLK_ADDRESS,
- },
-
- //
- // PM1a Control Register Block
- //
- {
- ACPI_PM1A_CNT_BLK_ADDRESS_SPACE_ID,
- ACPI_PM1A_CNT_BLK_BIT_WIDTH,
- ACPI_PM1A_CNT_BLK_BIT_OFFSET,
- 0x00,
- ACPI_PM1A_CNT_BLK_ADDRESS,
- },
-
- //
- // PM1b Control Register Block
- //
- {
- ACPI_PM1B_CNT_BLK_ADDRESS_SPACE_ID,
- ACPI_PM1B_CNT_BLK_BIT_WIDTH,
- ACPI_PM1B_CNT_BLK_BIT_OFFSET,
- 0x00,
- ACPI_PM1B_CNT_BLK_ADDRESS,
- },
-
- //
- // PM2 Control Register Block
- //
- {
- ACPI_PM2_CNT_BLK_ADDRESS_SPACE_ID,
- ACPI_PM2_CNT_BLK_BIT_WIDTH,
- ACPI_PM2_CNT_BLK_BIT_OFFSET,
- 0x00,
- ACPI_PM2_CNT_BLK_ADDRESS,
- },
-
- //
- // PM Timer Control Register Block
- //
- {
- ACPI_PM_TMR_BLK_ADDRESS_SPACE_ID,
- ACPI_PM_TMR_BLK_BIT_WIDTH,
- ACPI_PM_TMR_BLK_BIT_OFFSET,
- 0x00,
- ACPI_PM_TMR_BLK_ADDRESS,
- },
-
- //
- // General Purpose Event 0 Register Block
- //
- {
- ACPI_GPE0_BLK_ADDRESS_SPACE_ID,
- ACPI_GPE0_BLK_BIT_WIDTH,
- ACPI_GPE0_BLK_BIT_OFFSET,
- 0x00,
- ACPI_GPE0_BLK_ADDRESS,
- },
-
- //
- // General Purpose Event 1 Register Block
- //
- {
- ACPI_GPE1_BLK_ADDRESS_SPACE_ID,
- ACPI_GPE1_BLK_BIT_WIDTH,
- ACPI_GPE1_BLK_BIT_OFFSET,
- 0x00,
- ACPI_GPE1_BLK_ADDRESS
- }
-
-};
diff --git a/tools/firmware/acpi/acpi_fadt.h b/tools/firmware/acpi/acpi_fadt.h
deleted file mode 100644
index f30a1dac98..0000000000
--- a/tools/firmware/acpi/acpi_fadt.h
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * 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 _FADT_H_
-#define _FADT_H_
-
-#include <xen/hvm/ioreq.h>
-
-//
-// FADT Definitions, see ACPI 2.0 specification for details.
-//
-
-#define ACPI_OEM_FADT_REVISION 0x00000001 // TBD
-
-#define ACPI_PREFERRED_PM_PROFILE 0x00
-#define ACPI_SCI_INT 0x0009
-#define ACPI_SMI_CMD 0x00000000
-#define ACPI_ACPI_ENABLE 0x00
-#define ACPI_ACPI_DISABLE 0x00
-#define ACPI_S4_BIOS_REQ 0x00
-#define ACPI_PSTATE_CNT 0x00
-#define ACPI_GPE1_BASE 0x00
-#define ACPI_CST_CNT 0x00
-#define ACPI_P_LVL2_LAT 0x0064
-#define ACPI_P_LVL3_LAT 0X03E8
-#define ACPI_FLUSH_SIZE 0x00
-#define ACPI_FLUSH_STRIDE 0x00
-#define ACPI_DUTY_OFFSET 0x01
-#define ACPI_DUTY_WIDTH 0x00
-#define ACPI_DAY_ALRM 0x00
-#define ACPI_MON_ALRM 0x00
-#define ACPI_CENTURY 0x00
-
-//
-// IA-PC Boot Architecture Flags, see ACPI 2.0 table specification and Acpi2_0.h
-//
-#define ACPI_IAPC_BOOT_ARCH (ACPI_LEGACY_DEVICES | ACPI_8042)
-
-//
-// Fixed Feature Flags
-//
-#define ACPI_FIXED_FEATURE_FLAGS (ACPI_PROC_C1 | ACPI_SLP_BUTTON | \
- ACPI_WBINVD | ACPI_PWR_BUTTON | \
- ACPI_FIX_RTC | ACPI_TMR_VAL_EXT)
-
-//
-// PM1A Event Register Block Generic Address Information
-//
-#define ACPI_PM1A_EVT_BLK_ADDRESS_SPACE_ID ACPI_SYSTEM_IO
-#define ACPI_PM1A_EVT_BLK_BIT_WIDTH 0x20
-#define ACPI_PM1A_EVT_BLK_BIT_OFFSET 0x00
-
-//
-// PM1B Event Register Block Generic Address Information
-//
-#define ACPI_PM1B_EVT_BLK_ADDRESS_SPACE_ID ACPI_SYSTEM_IO
-#define ACPI_PM1B_EVT_BLK_BIT_WIDTH 0x00
-#define ACPI_PM1B_EVT_BLK_BIT_OFFSET 0x00
-#define ACPI_PM1B_EVT_BLK_ADDRESS 0x0000000000000000
-
-//
-// PM1A Control Register Block Generic Address Information
-//
-#define ACPI_PM1A_CNT_BLK_ADDRESS_SPACE_ID ACPI_SYSTEM_IO
-#define ACPI_PM1A_CNT_BLK_BIT_WIDTH 0x10
-#define ACPI_PM1A_CNT_BLK_BIT_OFFSET 0x00
-
-//
-// PM1B Control Register Block Generic Address Information
-//
-#define ACPI_PM1B_CNT_BLK_ADDRESS_SPACE_ID ACPI_SYSTEM_IO
-#define ACPI_PM1B_CNT_BLK_BIT_WIDTH 0x00
-#define ACPI_PM1B_CNT_BLK_BIT_OFFSET 0x00
-#define ACPI_PM1B_CNT_BLK_ADDRESS 0x0000000000000000
-
-//
-// PM2 Control Register Block Generic Address Information
-//
-#define ACPI_PM2_CNT_BLK_ADDRESS_SPACE_ID ACPI_SYSTEM_IO
-#define ACPI_PM2_CNT_BLK_BIT_WIDTH 0x00
-#define ACPI_PM2_CNT_BLK_BIT_OFFSET 0x00
-#define ACPI_PM2_CNT_BLK_ADDRESS 0x0000000000000000
-
-//
-// Power Management Timer Control Register Block Generic Address
-// Information
-//
-#define ACPI_PM_TMR_BLK_ADDRESS_SPACE_ID ACPI_SYSTEM_IO
-#define ACPI_PM_TMR_BLK_BIT_WIDTH 0x20
-#define ACPI_PM_TMR_BLK_BIT_OFFSET 0x00
-
-//
-// General Purpose Event 0 Register Block Generic Address
-// Information
-//
-
-#define ACPI_GPE0_BLK_ADDRESS_SPACE_ID ACPI_SYSTEM_IO
-#define ACPI_GPE0_BLK_BIT_WIDTH 0x00
-#define ACPI_GPE0_BLK_BIT_OFFSET 0x00
-#define ACPI_GPE0_BLK_ADDRESS 0x00
-
-//
-// General Purpose Event 1 Register Block Generic Address
-// Information
-//
-
-#define ACPI_GPE1_BLK_ADDRESS_SPACE_ID ACPI_SYSTEM_IO
-#define ACPI_GPE1_BLK_BIT_WIDTH 0x00
-#define ACPI_GPE1_BLK_BIT_OFFSET 0x00
-#define ACPI_GPE1_BLK_ADDRESS 0x00
-
-
-//
-// Reset Register Generic Address Information
-//
-#define ACPI_RESET_REG_ADDRESS_SPACE_ID ACPI_SYSTEM_IO
-#define ACPI_RESET_REG_BIT_WIDTH 0x08
-#define ACPI_RESET_REG_BIT_OFFSET 0x00
-#define ACPI_RESET_REG_ADDRESS 0x0000000000000CF9
-#define ACPI_RESET_VALUE 0x06
-
-//
-// Number of bytes decoded by PM1 event blocks (a and b)
-//
-#define ACPI_PM1_EVT_LEN ((ACPI_PM1A_EVT_BLK_BIT_WIDTH + ACPI_PM1B_EVT_BLK_BIT_WIDTH) / 8)
-
-//
-// Number of bytes decoded by PM1 control blocks (a and b)
-//
-#define ACPI_PM1_CNT_LEN ((ACPI_PM1A_CNT_BLK_BIT_WIDTH + ACPI_PM1B_CNT_BLK_BIT_WIDTH) / 8)
-
-//
-// Number of bytes decoded by PM2 control block
-//
-#define ACPI_PM2_CNT_LEN (ACPI_PM2_CNT_BLK_BIT_WIDTH / 8)
-
-//
-// Number of bytes decoded by PM timer block
-//
-#define ACPI_PM_TMR_LEN (ACPI_PM_TMR_BLK_BIT_WIDTH / 8)
-
-//
-// Number of bytes decoded by GPE0 block
-//
-#define ACPI_GPE0_BLK_LEN (ACPI_GPE0_BLK_BIT_WIDTH / 8)
-
-//
-// Number of bytes decoded by GPE1 block
-//
-#define ACPI_GPE1_BLK_LEN 0
-
-#endif
diff --git a/tools/firmware/acpi/acpi_madt.c b/tools/firmware/acpi/acpi_madt.c
deleted file mode 100644
index 7a0234f1f7..0000000000
--- a/tools/firmware/acpi/acpi_madt.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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.
- *
- */
-#include "acpi_madt.h"
-
-//
-// Multiple APIC Description Table
-//
-
-ACPI_MULTIPLE_APIC_DESCRIPTION_TABLE Madt = {
- {
- {
- ACPI_MULTIPLE_APIC_DESCRIPTION_TABLE_SIGNATURE,
- sizeof (ACPI_MULTIPLE_APIC_DESCRIPTION_TABLE),
- ACPI_2_0_MADT_REVISION,
- 0x00, // Checksum
- ACPI_OEM_ID,
- ACPI_OEM_TABLE_ID,
- ACPI_OEM_REVISION,
- ACPI_CREATOR_ID,
- ACPI_CREATOR_REVISION,
- },
- ACPI_LOCAL_APIC_ADDRESS,
- ACPI_MULTIPLE_APIC_FLAGS,
- },
-
- //
- // IO APIC
- //
- {
- {
- ACPI_IO_APIC,
- sizeof (ACPI_IO_APIC_STRUCTURE),
- 0x00,
- 0x00,
- ACPI_IO_APIC_ADDRESS_1,
- 0x0000
- }
- },
-
- //
- // LOCAL APIC Entries for up to 32 processors.
- //
- {
- {
- ACPI_PROCESSOR_LOCAL_APIC,
- sizeof (ACPI_LOCAL_APIC_STRUCTURE),
- 0x00,
- 0x00,
- 0x00000001,
- }
-
- }
-};
diff --git a/tools/firmware/acpi/acpi_rsdt.c b/tools/firmware/acpi/acpi_rsdt.c
deleted file mode 100644
index 56390ef2ca..0000000000
--- a/tools/firmware/acpi/acpi_rsdt.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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.
- *
- */
-#include "acpi2_0.h"
-
-ACPI_2_0_RSDT Rsdt={
- {
- ACPI_2_0_RSDT_SIGNATURE,
- sizeof (ACPI_TABLE_HEADER), // udpated later
- ACPI_2_0_RSDT_REVISION,
- 0x0, //Checksum, updated later
- ACPI_OEM_ID,
- ACPI_OEM_TABLE_ID,
- ACPI_OEM_REVISION,
- ACPI_CREATOR_ID,
- ACPI_CREATOR_REVISION,
- },
- {0x0, 0x0}
-};
-
-ACPI_2_0_XSDT Xsdt={
- {
- ACPI_2_0_XSDT_SIGNATURE,
- sizeof (ACPI_TABLE_HEADER), //update later
- ACPI_2_0_XSDT_REVISION,
- 0x0, //Checksum, update later
- ACPI_OEM_ID,
- ACPI_OEM_TABLE_ID,
- ACPI_OEM_REVISION,
- ACPI_CREATOR_ID,
- ACPI_CREATOR_REVISION,
- },
- {0x0, 0x0},
-};
-
-
-ACPI_2_0_RSDP Rsdp={
- ACPI_2_0_RSDP_SIGNATURE,
- 0x00, // Checksum, updated in later
- ACPI_OEM_ID, // OEM ID,
- ACPI_OEM_REVISION,
- 0x0, // RSDT address, updated later
- sizeof (ACPI_2_0_RSDP),
- 0x0, // XSDT address, updated later
- 0x0, // Extended Checksum, update later
- {
- 0x0, // Reserved
- 0x0, // Reserved
- 0x0, // Reserved
- }
-};
-
-
-
diff --git a/tools/firmware/hvmloader/Makefile b/tools/firmware/hvmloader/Makefile
index cb740a7d55..e66ba03b2a 100644
--- a/tools/firmware/hvmloader/Makefile
+++ b/tools/firmware/hvmloader/Makefile
@@ -40,27 +40,30 @@ OBJCOPY = objcopy
CFLAGS += $(DEFINES) -I. $(XENINC) -fno-builtin -O2 -msoft-float
LDFLAGS = -nostdlib -Wl,-N -Wl,-Ttext -Wl,$(LOADADDR)
-SRCS = hvmloader.c acpi_madt.c mp_tables.c util.c smbios.c
+SRCS = hvmloader.c acpi_madt.c mp_tables.c util.c smbios.c acpi_utils.c
OBJS = $(patsubst %.c,%.o,$(SRCS))
.PHONY: all
all: hvmloader
+acpi/acpi.bin:
+ $(MAKE) -C acpi
+
hvmloader: roms.h $(SRCS)
$(CC) $(CFLAGS) -c $(SRCS)
$(CC) $(CFLAGS) $(LDFLAGS) -o hvmloader.tmp $(OBJS)
$(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 ../vmxassist/vmxassist.bin ../acpi/acpi.bin
+roms.h: ../rombios/BIOS-bochs-latest ../vgabios/VGABIOS-lgpl-latest.bin ../vgabios/VGABIOS-lgpl-latest.cirrus.bin ../vmxassist/vmxassist.bin acpi/acpi.bin
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
sh ./mkhex vmxassist ../vmxassist/vmxassist.bin >> roms.h
- sh ./mkhex acpi ../acpi/acpi.bin >> roms.h
+ sh ./mkhex acpi acpi/acpi.bin >> roms.h
.PHONY: clean
clean:
rm -f roms.h acpi.h
rm -f hvmloader hvmloader.tmp hvmloader.o $(OBJS)
-
+ $(MAKE) -C acpi clean
diff --git a/tools/firmware/hvmloader/acpi/Makefile b/tools/firmware/hvmloader/acpi/Makefile
new file mode 100644
index 0000000000..90054de0e7
--- /dev/null
+++ b/tools/firmware/hvmloader/acpi/Makefile
@@ -0,0 +1,63 @@
+#
+# 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/Rules.mk
+
+HOSTCFLAGS += -I. -I.. -I$(XEN_ROOT)/tools/libxc
+
+C_SRC = build.c dsdt.c gen.c static_tables.c
+H_SRC = $(wildcard *.h)
+ACPI_GEN = acpigen
+ACPI_BIN = acpi.bin
+
+IASL_VER = acpica-unix-20050513
+IASL_URL = http://developer.intel.com/technology/iapc/acpi/downloads/$(IASL_VER).tar.gz
+
+vpath iasl $(PATH)
+all:$(ACPI_BIN)
+
+dsdt.c: dsdt.asl
+ $(MAKE) iasl
+ iasl -tc dsdt.asl
+ mv dsdt.hex dsdt.c
+ echo "int DsdtLen=sizeof(AmlCode);" >> dsdt.c
+ rm *.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://developer.intel.com/technology/iapc/acpi/downloads.htm"
+ @echo
+ wget $(IASL_URL)
+ tar xzf $(IASL_VER).tar.gz
+ make -C $(IASL_VER)/compiler
+ $(INSTALL_PROG) $(IASL_VER)/compiler/iasl /usr/bin/iasl
+
+$(ACPI_GEN): $(C_SRC) $(H_SRC)
+ $(HOSTCC) -o $(ACPI_GEN) $(HOSTCFLAGS) $(C_SRC)
+
+$(ACPI_BIN): $(ACPI_GEN)
+ ./$(ACPI_GEN) $(ACPI_BIN)
+
+clean:
+ rm -rf *.o $(ACPI_GEN) $(ACPI_BIN) $(IASL_VER)
+ rm -rf $(IASL_VER).tar.gz
+
+install: all
diff --git a/tools/firmware/acpi/README b/tools/firmware/hvmloader/acpi/README
index 210d5bac71..210d5bac71 100644
--- a/tools/firmware/acpi/README
+++ b/tools/firmware/hvmloader/acpi/README
diff --git a/tools/firmware/hvmloader/acpi/acpi2_0.h b/tools/firmware/hvmloader/acpi/acpi2_0.h
new file mode 100644
index 0000000000..eca845a785
--- /dev/null
+++ b/tools/firmware/hvmloader/acpi/acpi2_0.h
@@ -0,0 +1,324 @@
+/*
+ * 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_
+
+typedef unsigned char uint8_t;
+typedef signed char int8_t;
+typedef unsigned short uint16_t;
+typedef signed short int16_t;
+typedef unsigned int uint32_t;
+typedef signed int int32_t;
+#ifdef __i386__
+typedef unsigned long long uint64_t;
+typedef signed long long int64_t;
+#else
+typedef unsigned long uint64_t;
+typedef signed long int64_t;
+#endif
+
+#include <xen/xen.h>
+
+#pragma pack (1)
+
+/*
+ * Common ACPI header.
+ */
+struct acpi_header {
+ uint32_t signature;
+ uint32_t length;
+ uint8_t revision;
+ uint8_t checksum;
+ uint8_t oem_id[6];
+ uint64_t oem_table_id;
+ uint32_t oem_revision;
+ uint32_t creator_id;
+ uint32_t creator_revision;
+};
+
+#define ACPI_OEM_ID {'I','N','T','E','L',' '}
+#define ACPI_OEM_TABLE_ID 0x544244 /* "TBD" */
+#define ACPI_OEM_REVISION 0x00000002
+#define ACPI_CREATOR_ID 0x00 /* TBD */
+#define ACPI_CREATOR_REVISION 0x00000002
+
+/*
+ * 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;
+ uint8_t 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;
+ uint8_t 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[ACPI_MAX_NUM_TABLES];
+};
+#define ACPI_2_0_RSDT_REVISION 0x01
+
+/*
+ * Extended System Description Table (XSDT).
+ */
+struct acpi_20_xsdt {
+ struct acpi_header header;
+ uint64_t entry[ACPI_MAX_NUM_TABLES];
+};
+#define ACPI_2_0_XSDT_REVISION 0x01
+
+/*
+ * 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;
+};
+#define ACPI_2_0_FADT_REVISION 0x03
+
+/*
+ * 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_header {
+ struct acpi_header header;
+ uint32_t lapic_addr;
+ uint32_t flags;
+};
+
+#define ACPI_2_0_MADT_REVISION 0x01
+
+/*
+ * 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 {
+ struct acpi_20_madt_header header;
+ struct acpi_20_madt_ioapic io_apic[1];
+ struct acpi_20_madt_lapic lapic[32];
+};
+
+/*
+ * Table Signatures.
+ */
+#define ACPI_2_0_RSDP_SIGNATURE 0x2052545020445352LL /* "RSD PTR " */
+#define ACPI_2_0_FACS_SIGNATURE 0x53434146 /* "FACS" */
+#define ACPI_2_0_FADT_SIGNATURE 0x50434146 /* "FADT" */
+#define ACPI_2_0_MADT_SIGNATURE 0x43495041 /* "APIC" */
+#define ACPI_2_0_RSDT_SIGNATURE 0x54445352 /* "RSDT" */
+#define ACPI_2_0_XSDT_SIGNATURE 0x54445358 /* "XSDT" */
+
+#pragma pack ()
+
+#define ACPI_PHYSICAL_ADDRESS 0xEA000
+#define ACPI_TABLE_SIZE (4*1024)
+
+void AcpiBuildTable(uint8_t *buf);
+
+#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 0000000000..8c1bde2bf1
--- /dev/null
+++ b/tools/firmware/hvmloader/acpi/build.c
@@ -0,0 +1,241 @@
+/*
+ * 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.
+ *
+ */
+
+#include "acpi2_0.h"
+
+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_madt Madt;
+extern struct acpi_20_facs Facs;
+extern unsigned char *AmlCode;
+extern int DsdtLen;
+
+
+typedef struct _ACPI_TABLE_ALL{
+ struct acpi_20_rsdp *Rsdp;
+ struct acpi_20_rsdt *Rsdt;
+ struct acpi_20_xsdt *Xsdt;
+ struct acpi_20_fadt *Fadt;
+ struct acpi_20_madt *Madt;
+ struct acpi_20_facs *Facs;
+ unsigned char *Dsdt;
+ uint32_t RsdpOffset;
+ uint32_t RsdtOffset;
+ uint32_t XsdtOffset;
+ uint32_t FadtOffset;
+ uint32_t MadtOffset;
+ uint32_t FacsOffset;
+ uint32_t DsdtOffset;
+}ACPI_TABLE_ALL;
+
+static
+void
+MemCopy(void* src, void* dst, int len){
+
+ uint8_t* src0=src;
+ uint8_t* dst0=dst;
+
+ while(len--){
+ *(dst0++)=*(src0++);
+ }
+}
+
+static
+void
+SetCheckSum(
+ void* Table,
+ uint32_t ChecksumOffset,
+ uint32_t Length
+ )
+/*
+ * Routine Description:
+ * Calculate Checksum and store the result in the checksum
+ * filed of the table
+ *
+ * INPUT:
+ * Table: Start pointer of table
+ * ChecksumOffset: Offset of checksum field in the table
+ * Length: Length of Table
+ */
+{
+ uint8_t Sum = 0;
+ uint8_t *Ptr;
+
+ Ptr=Table;
+ Ptr[ChecksumOffset]=0;
+ while (Length--) {
+ Sum = (uint8_t)(Sum + (*Ptr++));
+ }
+
+ Ptr = Table;
+ Ptr[ChecksumOffset] = (uint8_t) (0xff - Sum + 1);
+}
+
+//
+// FIELD_OFFSET - returns the byte offset to a field within a structure
+//
+#define FIELD_OFFSET(TYPE,Field) ((uint32_t)(&(((TYPE *) 0)->Field)))
+
+static
+void
+UpdateTable(
+ ACPI_TABLE_ALL *table
+ )
+/*
+ * Update the ACPI table:
+ * fill in the actuall physical address of RSDT, XSDT, FADT, MADT, FACS
+ * Caculate the checksum
+ */
+{
+ // RSDP Update
+ table->Rsdp->rsdt_address = (uint32_t)(ACPI_PHYSICAL_ADDRESS+
+ table->RsdtOffset);
+ table->Rsdp->xsdt_address = (uint64_t)(ACPI_PHYSICAL_ADDRESS+
+ table->XsdtOffset);
+ SetCheckSum(table->Rsdp,
+ FIELD_OFFSET(struct acpi_10_rsdp, checksum),
+ sizeof(struct acpi_10_rsdp)
+ );
+ SetCheckSum(table->Rsdp,
+ FIELD_OFFSET(struct acpi_20_rsdp,
+ extended_checksum),
+ sizeof(struct acpi_20_rsdp)
+ );
+
+
+ //RSDT Update
+ table->Rsdt->entry[0] = (uint32_t)(ACPI_PHYSICAL_ADDRESS +
+ table->FadtOffset);
+ table->Rsdt->entry[1] = (uint32_t)(ACPI_PHYSICAL_ADDRESS +
+ table->MadtOffset);
+ table->Rsdt->header.length = sizeof (struct acpi_header) +
+ 2*sizeof(uint32_t);
+ SetCheckSum(table->Rsdt,
+ FIELD_OFFSET(struct acpi_header, checksum),
+ table->Rsdt->header.length
+ );
+
+ //XSDT Update
+ table->Xsdt->entry[0] = (uint64_t)(ACPI_PHYSICAL_ADDRESS +
+ table->FadtOffset);
+ table->Xsdt->entry[1] = (uint64_t)(ACPI_PHYSICAL_ADDRESS +
+ table->MadtOffset);
+ table->Xsdt->header.length = sizeof (struct acpi_header) +
+ 2*sizeof(uint64_t);
+ SetCheckSum(table->Xsdt,
+ FIELD_OFFSET(struct acpi_header, checksum),
+ table->Xsdt->header.length
+ );
+
+ // FADT Update
+ table->Fadt->dsdt = (uint32_t)(ACPI_PHYSICAL_ADDRESS +
+ table->DsdtOffset);
+ table->Fadt->x_dsdt = (uint64_t)(ACPI_PHYSICAL_ADDRESS +
+ table->DsdtOffset);
+ table->Fadt->firmware_ctrl = (uint32_t)(ACPI_PHYSICAL_ADDRESS +
+ table->FacsOffset);
+ table->Fadt->x_firmware_ctrl = (uint64_t)(ACPI_PHYSICAL_ADDRESS +
+ table->FacsOffset);
+ SetCheckSum(table->Fadt,
+ FIELD_OFFSET(struct acpi_header, checksum),
+ sizeof(struct acpi_20_fadt)
+ );
+
+ // MADT update
+ SetCheckSum(table->Madt,
+ FIELD_OFFSET(struct acpi_header, checksum),
+ sizeof(struct acpi_20_madt)
+ );
+}
+
+void
+AcpiBuildTable(uint8_t* buf)
+/*
+ * Copy all the ACPI table to buffer
+ * Buffer Layout:
+ * FACS
+ * RSDP
+ * RSDT
+ * XSDT
+ * FADT
+ * MADT
+ * DSDT
+ *
+ */
+{
+ ACPI_TABLE_ALL table;
+ int offset=0;
+
+ // FACS: should be 64-bit alignment
+ // so it is put at the start of buffer
+ // as the buffer is 64 bit alignment
+ table.FacsOffset = offset;
+ table.Facs = (struct acpi_20_facs *)(&buf[offset]);
+ MemCopy(&Facs, table.Facs, sizeof(struct acpi_20_facs));
+ offset += sizeof(struct acpi_20_facs);
+
+ // RSDP
+ table.RsdpOffset = offset;
+ table.Rsdp = (struct acpi_20_rsdp *)(&buf[offset]);
+ MemCopy(&Rsdp, table.Rsdp, sizeof(struct acpi_20_rsdp));
+ offset += sizeof(struct acpi_20_rsdp);
+
+ // RSDT
+ table.RsdtOffset = offset;
+ table.Rsdt = (struct acpi_20_rsdt *)(&buf[offset]);
+ MemCopy(&Rsdt, table.Rsdt, sizeof(struct acpi_20_rsdt));
+ offset += sizeof(struct acpi_20_rsdt);
+
+ // XSDT
+ table.XsdtOffset = offset;
+ table.Xsdt = (struct acpi_20_xsdt *)(&buf[offset]);
+ MemCopy(&Xsdt, table.Xsdt, sizeof(struct acpi_20_xsdt));
+ offset += sizeof(struct acpi_20_xsdt);
+
+ // FADT
+ table.FadtOffset = offset;
+ table.Fadt = (struct acpi_20_fadt *)(&buf[offset]);
+ MemCopy(&Fadt, table.Fadt, sizeof(struct acpi_20_fadt));
+ offset += sizeof(struct acpi_20_fadt);
+
+ // MADT
+ table.MadtOffset = offset;
+ table.Madt = (struct acpi_20_madt*)(&buf[offset]);
+ MemCopy(&Madt, table.Madt, sizeof(struct acpi_20_madt));
+ offset += sizeof(struct acpi_20_madt);
+
+ // DSDT
+ table.DsdtOffset = offset;
+ table.Dsdt = (unsigned char *)(&buf[offset]);
+ MemCopy(&AmlCode, table.Dsdt, DsdtLen);
+ offset += DsdtLen;
+
+ UpdateTable(&table);
+}
+
+/*
+ * 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/acpi/acpi_dsdt.asl b/tools/firmware/hvmloader/acpi/dsdt.asl
index 2ea34e7d12..2ea34e7d12 100644
--- a/tools/firmware/acpi/acpi_dsdt.asl
+++ b/tools/firmware/hvmloader/acpi/dsdt.asl
diff --git a/tools/firmware/acpi/acpi_dsdt.c b/tools/firmware/hvmloader/acpi/dsdt.c
index e8c5ec7805..e8c5ec7805 100644
--- a/tools/firmware/acpi/acpi_dsdt.c
+++ b/tools/firmware/hvmloader/acpi/dsdt.c
diff --git a/tools/firmware/acpi/acpi_gen.c b/tools/firmware/hvmloader/acpi/gen.c
index 25c0ca7917..25c0ca7917 100644
--- a/tools/firmware/acpi/acpi_gen.c
+++ b/tools/firmware/hvmloader/acpi/gen.c
diff --git a/tools/firmware/hvmloader/acpi/static_tables.c b/tools/firmware/hvmloader/acpi/static_tables.c
new file mode 100644
index 0000000000..169d2f8848
--- /dev/null
+++ b/tools/firmware/hvmloader/acpi/static_tables.c
@@ -0,0 +1,184 @@
+/*
+ * 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 <xen/hvm/ioreq.h>
+
+/*
+ * Multiple APIC Description Table (MADT).
+ */
+
+struct acpi_20_madt Madt = {
+ .header = {
+ .header = {
+ .signature = ACPI_2_0_MADT_SIGNATURE,
+ .length = sizeof(struct acpi_20_madt),
+ .revision = ACPI_2_0_MADT_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
+ },
+ .lapic_addr = 0xFEE00000,
+ .flags = ACPI_PCAT_COMPAT
+ },
+
+ /* IO APIC */
+ .io_apic = {
+ [0] = {
+ .type = ACPI_IO_APIC,
+ .length = sizeof(struct acpi_20_madt_ioapic),
+ .ioapic_addr = 0xFEC00000
+ }
+ },
+
+ /* Local APIC entries for up to 32 processors. */
+ .lapic = {
+ [0] = {
+ .type = ACPI_PROCESSOR_LOCAL_APIC,
+ .length = sizeof(struct acpi_20_madt_lapic),
+ .flags = 0x00000001
+ }
+ }
+};
+
+
+/*
+ * 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,
+ .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,
+
+ .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_OEM_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/acpi_madt.c b/tools/firmware/hvmloader/acpi_madt.c
index 5a9c3063ae..f19f0ab556 100644
--- a/tools/firmware/hvmloader/acpi_madt.c
+++ b/tools/firmware/hvmloader/acpi_madt.c
@@ -18,9 +18,9 @@
* Place - Suite 330, Boston, MA 02111-1307 USA.
*/
-#include "../acpi/acpi2_0.h"
-#include "../acpi/acpi_madt.h"
+#include "acpi/acpi2_0.h"
#include "util.h"
+#include "acpi_utils.h"
#include <xen/hvm/hvm_info_table.h>
#define NULL ((void*)0)
@@ -29,160 +29,134 @@ static struct hvm_info_table *table = NULL;
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]) {
- puts("Bad hvm info signature\n");
- return 0;
- }
- }
-
- for (i = 0; i < t->length; i++)
- sum += ptr[i];
-
- return (sum == 0);
+ 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]) {
+ puts("Bad hvm info signature\n");
+ return 0;
+ }
+ }
+
+ for (i = 0; i < t->length; i++)
+ sum += ptr[i];
+
+ return (sum == 0);
}
/* xc_vmx_builder wrote hvm info at 0x9F800. Return it. */
struct hvm_info_table *
get_hvm_info_table(void)
{
- struct hvm_info_table *t;
+ struct hvm_info_table *t;
- if (table != NULL)
- return table;
+ if (table != NULL)
+ return table;
- t = (struct hvm_info_table *)HVM_INFO_PADDR;
+ t = (struct hvm_info_table *)HVM_INFO_PADDR;
- if (!validate_hvm_info(t)) {
- puts("Bad hvm info table\n");
- return NULL;
- }
+ if (!validate_hvm_info(t)) {
+ puts("Bad hvm info table\n");
+ return NULL;
+ }
- table = t;
+ table = t;
- return table;
+ return table;
}
int
get_vcpu_nr(void)
{
- struct hvm_info_table *t = get_hvm_info_table();
- return (t ? t->nr_vcpus : 1); /* default 1 vcpu */
+ struct hvm_info_table *t = get_hvm_info_table();
+ return (t ? t->nr_vcpus : 1); /* default 1 vcpu */
}
int
get_acpi_enabled(void)
{
- struct hvm_info_table *t = get_hvm_info_table();
- return (t ? t->acpi_enabled : 0); /* default no acpi */
+ struct hvm_info_table *t = get_hvm_info_table();
+ return (t ? t->acpi_enabled : 0); /* default no acpi */
}
static void *
acpi_madt_get_madt(unsigned char *acpi_start)
{
- ACPI_2_0_RSDP *rsdp=NULL;
- ACPI_2_0_RSDT *rsdt=NULL;
- ACPI_MULTIPLE_APIC_DESCRIPTION_TABLE *madt;
-
- rsdp = (ACPI_2_0_RSDP *)(acpi_start + sizeof(ACPI_2_0_FACS));
- if (rsdp->Signature != ACPI_2_0_RSDP_SIGNATURE) {
- puts("Bad RSDP signature\n");
- return NULL;
- }
-
- rsdt= (ACPI_2_0_RSDT *)
- (acpi_start + rsdp->RsdtAddress - ACPI_PHYSICAL_ADDRESS);
- if (rsdt->Header.Signature != ACPI_2_0_RSDT_SIGNATURE) {
- puts("Bad RSDT signature\n");
- return NULL;
- }
-
- madt = (ACPI_MULTIPLE_APIC_DESCRIPTION_TABLE *)
- ( acpi_start+ rsdt->Entry[1] - ACPI_PHYSICAL_ADDRESS);
- if (madt->Header.Header.Signature !=
- ACPI_MULTIPLE_APIC_DESCRIPTION_TABLE_SIGNATURE) {
- puts("Bad MADT signature \n");
- return NULL;
- }
-
- return madt;
-}
+ struct acpi_20_rsdt *rsdt;
+ struct acpi_20_madt *madt;
-static void
-set_checksum(void *start, int checksum_offset, int len)
-{
- unsigned char sum = 0;
- unsigned char *ptr;
+ rsdt = acpi_rsdt_get(acpi_start);
+ if (rsdt == NULL)
+ return NULL;
- ptr = start;
- ptr[checksum_offset] = 0;
- while (len--)
- sum += *ptr++;
+ madt = (struct acpi_20_madt *)(acpi_start + rsdt->entry[1] -
+ ACPI_PHYSICAL_ADDRESS);
+ if (madt->header.header.signature != ACPI_2_0_MADT_SIGNATURE) {
+ puts("Bad MADT signature \n");
+ return NULL;
+ }
- ptr = start;
- ptr[checksum_offset] = -sum;
+ return madt;
}
static int
acpi_madt_set_local_apics(
- int nr_vcpu,
- ACPI_MULTIPLE_APIC_DESCRIPTION_TABLE *madt)
+ int nr_vcpu,
+ struct acpi_20_madt *madt)
{
- int i;
+ int i;
- if ((nr_vcpu > MAX_VIRT_CPUS) || (nr_vcpu < 0) || !madt)
- return -1;
+ if ((nr_vcpu > MAX_VIRT_CPUS) || (nr_vcpu < 0) || !madt)
+ return -1;
- for (i = 0; i < nr_vcpu; i++) {
- madt->LocalApic[i].Type = ACPI_PROCESSOR_LOCAL_APIC;
- madt->LocalApic[i].Length = sizeof (ACPI_LOCAL_APIC_STRUCTURE);
- madt->LocalApic[i].AcpiProcessorId = i;
- madt->LocalApic[i].ApicId = i;
- madt->LocalApic[i].Flags = 1;
- }
+ for (i = 0; i < nr_vcpu; i++) {
+ madt->lapic[i].type = ACPI_PROCESSOR_LOCAL_APIC;
+ madt->lapic[i].length = sizeof(struct acpi_20_madt_lapic);
+ madt->lapic[i].acpi_processor_id = i;
+ madt->lapic[i].apic_id = i;
+ madt->lapic[i].flags = 1;
+ }
- madt->Header.Header.Length =
- sizeof(ACPI_MULTIPLE_APIC_DESCRIPTION_TABLE) -
- (MAX_VIRT_CPUS - nr_vcpu)* sizeof(ACPI_LOCAL_APIC_STRUCTURE);
+ madt->header.header.length =
+ sizeof(struct acpi_20_madt) -
+ (MAX_VIRT_CPUS - nr_vcpu) * sizeof(struct acpi_20_madt_lapic);
- return 0;
+ return 0;
}
#define FIELD_OFFSET(TYPE,Field) ((unsigned int)(&(((TYPE *) 0)->Field)))
int acpi_madt_update(unsigned char *acpi_start)
{
- int rc;
- ACPI_MULTIPLE_APIC_DESCRIPTION_TABLE *madt;
+ int rc;
+ struct acpi_20_madt *madt;
- madt = acpi_madt_get_madt(acpi_start);
- if (!madt)
- return -1;
+ madt = acpi_madt_get_madt(acpi_start);
+ if (!madt)
+ return -1;
- rc = acpi_madt_set_local_apics(get_vcpu_nr(), madt);
- if (rc != 0)
- return rc;
+ rc = acpi_madt_set_local_apics(get_vcpu_nr(), madt);
+ if (rc != 0)
+ return rc;
- set_checksum(
- madt, FIELD_OFFSET(ACPI_TABLE_HEADER, Checksum),
- madt->Header.Header.Length);
+ set_checksum(
+ madt, FIELD_OFFSET(struct acpi_header, checksum),
+ madt->header.header.length);
- return 0;
+ return 0;
}
/*
* Local variables:
- * c-file-style: "linux"
- * indent-tabs-mode: t
- * c-indent-level: 8
- * c-basic-offset: 8
- * tab-width: 8
+ * 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_ssdt_tpm.asl b/tools/firmware/hvmloader/acpi_ssdt_tpm.asl
new file mode 100644
index 0000000000..98010a7f14
--- /dev/null
+++ b/tools/firmware/hvmloader/acpi_ssdt_tpm.asl
@@ -0,0 +1,29 @@
+//**********************************************************************//
+//*
+//* Copyright (c) 2006, IBM 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.
+
+//* SSDT for TPM TIS Interface for Xen with Qemu device model
+
+DefinitionBlock ("SSDT_TPM.aml", "SSDT", 1, "IBM","xen", 2006)
+{
+ Device (TPM) {
+ Name (_HID, EisaId ("PNP0C31"))
+ Name (_CRS, ResourceTemplate ()
+ {
+ Memory32Fixed (ReadWrite, 0xFED40000, 0x5000,)
+ })
+ }
+} \ No newline at end of file
diff --git a/tools/firmware/hvmloader/acpi_ssdt_tpm.h b/tools/firmware/hvmloader/acpi_ssdt_tpm.h
new file mode 100644
index 0000000000..9d943a3a36
--- /dev/null
+++ b/tools/firmware/hvmloader/acpi_ssdt_tpm.h
@@ -0,0 +1,25 @@
+/*
+ *
+ * Intel ACPI Component Architecture
+ * ASL Optimizing Compiler version 20060707 [Sep 11 2006]
+ * Copyright (C) 2000 - 2006 Intel Corporation
+ * Supports ACPI Specification Revision 3.0a
+ *
+ * Compilation of "acpi_ssdt_tpm.asl" - Mon Oct 30 11:28:27 2006
+ *
+ * C source code output
+ *
+ */
+unsigned char AmlCode_TPM[] =
+{
+ 0x53,0x53,0x44,0x54,0x4C,0x00,0x00,0x00, /* 00000000 "SSDTL..." */
+ 0x01,0x6D,0x49,0x42,0x4D,0x00,0x00,0x00, /* 00000008 ".mIBM..." */
+ 0x78,0x65,0x6E,0x00,0x00,0x00,0x00,0x00, /* 00000010 "xen....." */
+ 0xD6,0x07,0x00,0x00,0x49,0x4E,0x54,0x4C, /* 00000018 "....INTL" */
+ 0x07,0x07,0x06,0x20,0x5B,0x82,0x26,0x54, /* 00000020 "... [.&T" */
+ 0x50,0x4D,0x5F,0x08,0x5F,0x48,0x49,0x44, /* 00000028 "PM_._HID" */
+ 0x0C,0x41,0xD0,0x0C,0x31,0x08,0x5F,0x43, /* 00000030 ".A..1._C" */
+ 0x52,0x53,0x11,0x11,0x0A,0x0E,0x86,0x09, /* 00000038 "RS......" */
+ 0x00,0x01,0x00,0x00,0xD4,0xFE,0x00,0x50, /* 00000040 ".......P" */
+ 0x00,0x00,0x79,0x00,
+};
diff --git a/tools/firmware/hvmloader/acpi_utils.c b/tools/firmware/hvmloader/acpi_utils.c
new file mode 100644
index 0000000000..ad3616b69a
--- /dev/null
+++ b/tools/firmware/hvmloader/acpi_utils.c
@@ -0,0 +1,207 @@
+/*
+ * Commonly used ACPI utility functions.
+ * Probing for devices and writing SSDT entries into XSDT and RSDT tables.
+ *
+ * Yu Ke, ke.yu@intel.com
+ * Copyright (c) 2005, Intel Corporation.
+ * Copyright (c) 2006, IBM 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 "acpi/acpi2_0.h"
+#include "acpi_utils.h"
+#include "util.h"
+
+static int acpi_rsdt_add_entry_pointer(unsigned char *acpi_start,
+ unsigned char *entry);
+static unsigned char *acpi_xsdt_add_entry(unsigned char *acpi_start,
+ unsigned char **freemem,
+ unsigned char *limit,
+ unsigned char *table,
+ unsigned int table_size);
+
+void set_checksum(void *start, int checksum_offset, int len)
+{
+ unsigned char sum = 0;
+ unsigned char *ptr;
+
+ ptr = start;
+ ptr[checksum_offset] = 0;
+ while (len--)
+ sum += *ptr++;
+
+ ptr = start;
+ ptr[checksum_offset] = -sum;
+}
+
+
+#include "acpi_ssdt_tpm.h"
+static int acpi_tpm_tis_probe(unsigned char *acpi_start,
+ unsigned char **freemem,
+ unsigned char *limit)
+{
+ int success = 1; /* not successful means 'out of memory' */
+ unsigned char *addr;
+ /* check TPM_DID, TPM_VID, TPM_RID in ioemu/hw/tpm_tis.c */
+ uint16_t tis_did_vid_rid[] = {0x0001, 0x0001, 0x0001};
+
+ /* probe for TIS interface ... */
+ if (memcmp((char *)(0xFED40000 + 0xF00),
+ tis_did_vid_rid,
+ sizeof(tis_did_vid_rid)) == 0) {
+ puts("TIS is available\n");
+ addr = acpi_xsdt_add_entry(acpi_start, freemem, limit,
+ AmlCode_TPM, sizeof(AmlCode_TPM));
+ if (addr == NULL)
+ success = 0;
+ else {
+ /* legacy systems need an RSDT entry */
+ acpi_rsdt_add_entry_pointer(acpi_start,
+ addr);
+ }
+ }
+ return success;
+}
+
+
+/*
+ * Call functions that probe for devices and have them register their
+ * SSDT entries with the XSDT and RSDT tables.
+ */
+void acpi_update(unsigned char *acpi_start,
+ unsigned long acpi_size,
+ unsigned char *limit,
+ unsigned char **freemem)
+{
+ acpi_tpm_tis_probe(acpi_start, freemem, limit);
+}
+
+
+struct acpi_20_rsdt *acpi_rsdt_get(unsigned char *acpi_start)
+{
+ struct acpi_20_rsdp *rsdp;
+ struct acpi_20_rsdt *rsdt;
+
+ rsdp = (struct acpi_20_rsdp *)(acpi_start + sizeof(struct acpi_20_facs));
+ if (rsdp->signature != ACPI_2_0_RSDP_SIGNATURE) {
+ puts("Bad RSDP signature\n");
+ return NULL;
+ }
+
+ rsdt = (struct acpi_20_rsdt *)
+ (acpi_start + rsdp->rsdt_address - ACPI_PHYSICAL_ADDRESS);
+ if (rsdt->header.signature != ACPI_2_0_RSDT_SIGNATURE) {
+ puts("Bad RSDT signature\n");
+ return NULL;
+ }
+ return rsdt;
+}
+
+/*
+ * Add an entry to the RSDT table given the pointer to the entry.
+ */
+static int acpi_rsdt_add_entry_pointer(unsigned char *acpi_start,
+ unsigned char *entry)
+{
+ struct acpi_20_rsdt *rsdt = acpi_rsdt_get(acpi_start);
+ int found = 0;
+ int i = 0;
+
+ /* get empty slot in the RSDT table */
+ while (i < ACPI_MAX_NUM_TABLES) {
+ if (rsdt->entry[i] == 0) {
+ found = 1;
+ break;
+ }
+ i++;
+ }
+
+ if (found) {
+ rsdt->entry[i] = (uint64_t)(long)entry;
+ rsdt->header.length =
+ sizeof(struct acpi_header) +
+ (i + 1) * sizeof(uint64_t);
+ set_checksum(rsdt,
+ FIELD_OFFSET(struct acpi_header, checksum),
+ rsdt->header.length);
+ }
+
+ return found;
+}
+
+/* Get the XSDT table */
+struct acpi_20_xsdt *acpi_xsdt_get(unsigned char *acpi_start)
+{
+ struct acpi_20_rsdp *rsdp;
+ struct acpi_20_xsdt *xsdt;
+
+ rsdp = (struct acpi_20_rsdp *)(acpi_start + sizeof(struct acpi_20_facs));
+ if (rsdp->signature != ACPI_2_0_RSDP_SIGNATURE) {
+ puts("Bad RSDP signature\n");
+ return NULL;
+ }
+
+ xsdt = (struct acpi_20_xsdt *)
+ (acpi_start + rsdp->xsdt_address - ACPI_PHYSICAL_ADDRESS);
+ if (xsdt->header.signature != ACPI_2_0_XSDT_SIGNATURE) {
+ puts("Bad XSDT signature\n");
+ return NULL;
+ }
+ return xsdt;
+}
+
+/*
+ add an entry to the xdst table entry pointers
+ copy the given ssdt data to the current available memory at
+ freemem, if it does not exceed the limit
+ */
+static unsigned char *acpi_xsdt_add_entry(unsigned char *acpi_start,
+ unsigned char **freemem,
+ unsigned char *limit,
+ unsigned char *table,
+ unsigned int table_size)
+{
+ struct acpi_20_xsdt *xsdt = acpi_xsdt_get(acpi_start);
+ int found = 0, i = 0;
+ unsigned char *addr = NULL;
+
+ /* get empty slot in the Xsdt table */
+ while (i < ACPI_MAX_NUM_TABLES) {
+ if (xsdt->entry[i] == 0) {
+ found = 1;
+ break;
+ }
+ i++;
+ }
+
+ if (found) {
+ /* memory below hard limit ? */
+ if (*freemem + table_size <= limit) {
+ puts("Copying SSDT entry!\n");
+ addr = *freemem;
+ memcpy(addr, table, table_size);
+ xsdt->entry[i] = (uint64_t)(long)addr;
+ *freemem += table_size;
+ /* update the XSDT table */
+ xsdt->header.length =
+ sizeof(struct acpi_header) +
+ (i + 1) * sizeof(uint64_t);
+ set_checksum(xsdt,
+ FIELD_OFFSET(struct acpi_header, checksum),
+ xsdt->header.length);
+ }
+ }
+ return addr;
+}
diff --git a/tools/firmware/acpi/acpi_madt.h b/tools/firmware/hvmloader/acpi_utils.h
index 042d3ff1a2..814fa3229d 100644
--- a/tools/firmware/acpi/acpi_madt.h
+++ b/tools/firmware/hvmloader/acpi_utils.h
@@ -1,5 +1,8 @@
/*
- * Copyright (c) 2004, Intel Corporation.
+ * Commonly used ACPI utility functions.
+ *
+ * Yu Ke, ke.yu@intel.com
+ * Copyright (c) 2005, 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,
@@ -13,32 +16,21 @@
* 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 _MADT_H_
-#define _MADT_H_
-
-#include "acpi2_0.h"
-
-//
-// MADT Definitions, see ACPI 2.0 specification for details
-//
+#ifndef ACPI_UTILS_H
+#define ACPI_UTILS_H
-#define ACPI_LOCAL_APIC_ADDRESS 0xFEE00000
+#define FIELD_OFFSET(TYPE,Field) ((unsigned int)(&(((TYPE *) 0)->Field)))
-#define ACPI_MULTIPLE_APIC_FLAGS (ACPI_PCAT_COMPAT)
+#define NULL ((void*)0)
-#define ACPI_IO_APIC_ADDRESS_1 0xFEC00000
+void set_checksum(void *start, int checksum_offset, int len);
+void acpi_update(unsigned char *acpi_start,
+ unsigned long acpi_size,
+ unsigned char *limit,
+ unsigned char **freemem);
-//
-// MADT structure
-//
-#pragma pack (1)
-typedef struct {
- ACPI_2_0_MADT Header;
- ACPI_IO_APIC_STRUCTURE IoApic[1];
- ACPI_LOCAL_APIC_STRUCTURE LocalApic[32];
-} ACPI_MULTIPLE_APIC_DESCRIPTION_TABLE;
-#pragma pack ()
+struct acpi_20_rsdt *acpi_rsdt_get(unsigned char *acpi_start);
+struct acpi_20_xsdt *acpi_xsdt_get(unsigned char *acpi_start);
#endif
diff --git a/tools/firmware/hvmloader/hvmloader.c b/tools/firmware/hvmloader/hvmloader.c
index b563ff1f97..49a870bb0d 100644
--- a/tools/firmware/hvmloader/hvmloader.c
+++ b/tools/firmware/hvmloader/hvmloader.c
@@ -22,9 +22,10 @@
* Place - Suite 330, Boston, MA 02111-1307 USA.
*/
#include "roms.h"
-#include "../acpi/acpi2_0.h" /* for ACPI_PHYSICAL_ADDRESS */
+#include "acpi/acpi2_0.h" /* for ACPI_PHYSICAL_ADDRESS */
#include "hypercall.h"
#include "util.h"
+#include "acpi_utils.h"
#include "smbios.h"
#include <xen/version.h>
#include <xen/hvm/params.h>
@@ -164,8 +165,6 @@ init_hypercalls(void)
int
main(void)
{
- struct xen_hvm_param hvm_param;
-
puts("HVM Loader\n");
init_hypercalls();
@@ -176,10 +175,7 @@ main(void)
puts("Loading ROMBIOS ...\n");
memcpy((void *)ROMBIOS_PHYSICAL_ADDRESS, rombios, sizeof(rombios));
- hvm_param.domid = DOMID_SELF;
- hvm_param.index = HVM_PARAM_APIC_ENABLED;
- if (!hypercall_hvm_op(HVMOP_get_param, &hvm_param) && hvm_param.value)
- create_mp_tables();
+ create_mp_tables();
if (cirrus_check()) {
puts("Loading Cirrus VGABIOS ...\n");
@@ -195,12 +191,18 @@ main(void)
puts("Loading ACPI ...\n");
acpi_madt_update((unsigned char *) acpi);
if (ACPI_PHYSICAL_ADDRESS+sizeof(acpi) <= 0xF0000) {
+ unsigned char *freemem = (unsigned char *)
+ (ACPI_PHYSICAL_ADDRESS + sizeof(acpi));
/*
* Make sure acpi table does not overlap rombios
* currently acpi less than 8K will be OK.
*/
memcpy((void *)ACPI_PHYSICAL_ADDRESS, acpi,
sizeof(acpi));
+ acpi_update((unsigned char *)ACPI_PHYSICAL_ADDRESS,
+ sizeof(acpi),
+ (unsigned char *)0xF0000,
+ &freemem);
}
}
diff --git a/tools/firmware/hvmloader/util.c b/tools/firmware/hvmloader/util.c
index 2ce5367fb9..85cbcbe82f 100644
--- a/tools/firmware/hvmloader/util.c
+++ b/tools/firmware/hvmloader/util.c
@@ -18,7 +18,7 @@
* Place - Suite 330, Boston, MA 02111-1307 USA.
*/
-#include "../acpi/acpi2_0.h" /* for ACPI_PHYSICAL_ADDRESS */
+#include "acpi/acpi2_0.h" /* for ACPI_PHYSICAL_ADDRESS */
#include "util.h"
#include <stdint.h>
@@ -227,4 +227,5 @@ uuid_to_string(char *dest, uint8_t *uuid)
byte_to_hex(p, uuid[i]);
p += 2;
}
+ *p = 0;
}
diff --git a/tools/firmware/vmxassist/setup.c b/tools/firmware/vmxassist/setup.c
index 07ef70a1df..4f82ca0d9e 100644
--- a/tools/firmware/vmxassist/setup.c
+++ b/tools/firmware/vmxassist/setup.c
@@ -53,13 +53,10 @@ unsigned pgd[NR_PGD] __attribute__ ((aligned(PGSIZE))) = { 0 };
struct e820entry e820map[] = {
{ 0x0000000000000000ULL, 0x000000000009F800ULL, E820_RAM },
{ 0x000000000009F800ULL, 0x0000000000000800ULL, E820_RESERVED },
- { 0x00000000000A0000ULL, 0x0000000000020000ULL, E820_IO },
{ 0x00000000000C0000ULL, 0x0000000000040000ULL, E820_RESERVED },
{ 0x0000000000100000ULL, 0x0000000000000000ULL, E820_RAM },
- { 0x0000000000000000ULL, 0x0000000000001000ULL, E820_SHARED_PAGE },
{ 0x0000000000000000ULL, 0x0000000000003000ULL, E820_NVS },
{ 0x0000000000003000ULL, 0x000000000000A000ULL, E820_ACPI },
- { 0x00000000FEC00000ULL, 0x0000000001400000ULL, E820_IO },
};
#endif /* TEST */
diff --git a/tools/firmware/vmxassist/vm86.c b/tools/firmware/vmxassist/vm86.c
index f997fc22ab..e2cce6f379 100644
--- a/tools/firmware/vmxassist/vm86.c
+++ b/tools/firmware/vmxassist/vm86.c
@@ -867,6 +867,18 @@ load_seg(unsigned long sel, uint32_t *base, uint32_t *limit, union vmcs_arbytes
}
/*
+ * Emulate a protected mode segment load, falling back to clearing it if
+ * the descriptor was invalid.
+ */
+static void
+load_or_clear_seg(unsigned long sel, uint32_t *base, uint32_t *limit, union vmcs_arbytes *arbytes)
+{
+ if (!load_seg(sel, base, limit, arbytes))
+ load_seg(0, base, limit, arbytes);
+}
+
+
+/*
* Transition to protected mode
*/
static void
@@ -878,63 +890,22 @@ protected_mode(struct regs *regs)
oldctx.esp = regs->uesp;
oldctx.eflags = regs->eflags;
- memset(&saved_rm_regs, 0, sizeof(struct regs));
-
/* reload all segment registers */
if (!load_seg(regs->cs, &oldctx.cs_base,
&oldctx.cs_limit, &oldctx.cs_arbytes))
panic("Invalid %%cs=0x%x for protected mode\n", regs->cs);
oldctx.cs_sel = regs->cs;
- if (load_seg(regs->ves, &oldctx.es_base,
- &oldctx.es_limit, &oldctx.es_arbytes))
- oldctx.es_sel = regs->ves;
- else {
- load_seg(0, &oldctx.es_base,
- &oldctx.es_limit, &oldctx.es_arbytes);
- oldctx.es_sel = 0;
- saved_rm_regs.ves = regs->ves;
- }
-
- if (load_seg(regs->uss, &oldctx.ss_base,
- &oldctx.ss_limit, &oldctx.ss_arbytes))
- oldctx.ss_sel = regs->uss;
- else {
- load_seg(0, &oldctx.ss_base,
- &oldctx.ss_limit, &oldctx.ss_arbytes);
- oldctx.ss_sel = 0;
- saved_rm_regs.uss = regs->uss;
- }
-
- if (load_seg(regs->vds, &oldctx.ds_base,
- &oldctx.ds_limit, &oldctx.ds_arbytes))
- oldctx.ds_sel = regs->vds;
- else {
- load_seg(0, &oldctx.ds_base,
- &oldctx.ds_limit, &oldctx.ds_arbytes);
- oldctx.ds_sel = 0;
- saved_rm_regs.vds = regs->vds;
- }
-
- if (load_seg(regs->vfs, &oldctx.fs_base,
- &oldctx.fs_limit, &oldctx.fs_arbytes))
- oldctx.fs_sel = regs->vfs;
- else {
- load_seg(0, &oldctx.fs_base,
- &oldctx.fs_limit, &oldctx.fs_arbytes);
- oldctx.fs_sel = 0;
- saved_rm_regs.vfs = regs->vfs;
- }
-
- if (load_seg(regs->vgs, &oldctx.gs_base,
- &oldctx.gs_limit, &oldctx.gs_arbytes))
- oldctx.gs_sel = regs->vgs;
- else {
- load_seg(0, &oldctx.gs_base,
- &oldctx.gs_limit, &oldctx.gs_arbytes);
- oldctx.gs_sel = 0;
- saved_rm_regs.vgs = regs->vgs;
- }
+ load_or_clear_seg(oldctx.es_sel, &oldctx.es_base,
+ &oldctx.es_limit, &oldctx.es_arbytes);
+ load_or_clear_seg(oldctx.ss_sel, &oldctx.ss_base,
+ &oldctx.ss_limit, &oldctx.ss_arbytes);
+ load_or_clear_seg(oldctx.ds_sel, &oldctx.ds_base,
+ &oldctx.ds_limit, &oldctx.ds_arbytes);
+ load_or_clear_seg(oldctx.fs_sel, &oldctx.fs_base,
+ &oldctx.fs_limit, &oldctx.fs_arbytes);
+ load_or_clear_seg(oldctx.gs_sel, &oldctx.gs_base,
+ &oldctx.gs_limit, &oldctx.gs_arbytes);
/* initialize jump environment to warp back to protected mode */
regs->cs = CODE_SELECTOR;
@@ -1022,6 +993,16 @@ set_mode(struct regs *regs, enum vm86_mode newmode)
case VM86_REAL_TO_PROTECTED:
if (mode == VM86_REAL) {
regs->eflags |= EFLAGS_TF;
+ saved_rm_regs.vds = regs->vds;
+ saved_rm_regs.ves = regs->ves;
+ saved_rm_regs.vfs = regs->vfs;
+ saved_rm_regs.vgs = regs->vgs;
+ saved_rm_regs.uss = regs->uss;
+ oldctx.ds_sel = 0;
+ oldctx.es_sel = 0;
+ oldctx.fs_sel = 0;
+ oldctx.gs_sel = 0;
+ oldctx.ss_sel = 0;
break;
} else if (mode == VM86_REAL_TO_PROTECTED) {
break;
@@ -1282,6 +1263,10 @@ opcode(struct regs *regs)
else
regs->ves = pop16(regs);
TRACE((regs, regs->eip - eip, "pop %%es"));
+ if (mode == VM86_REAL_TO_PROTECTED) {
+ saved_rm_regs.ves = 0;
+ oldctx.es_sel = regs->ves;
+ }
return OPC_EMULATED;
case 0x0F: /* two byte opcode */
diff --git a/tools/ioemu/Makefile.target b/tools/ioemu/Makefile.target
index ca8dae4c4e..2ca5ce74bb 100644
--- a/tools/ioemu/Makefile.target
+++ b/tools/ioemu/Makefile.target
@@ -368,6 +368,7 @@ VL_OBJS+= usb-uhci.o
VL_OBJS+= piix4acpi.o
VL_OBJS+= xenstore.o
VL_OBJS+= xen_platform.o
+VL_OBJS+= tpm_tis.o
DEFINES += -DHAS_AUDIO
endif
ifeq ($(TARGET_BASE_ARCH), ppc)
diff --git a/tools/ioemu/hw/ne2000.c b/tools/ioemu/hw/ne2000.c
index e23c6df069..5fe383fb2a 100644
--- a/tools/ioemu/hw/ne2000.c
+++ b/tools/ioemu/hw/ne2000.c
@@ -137,6 +137,7 @@ typedef struct NE2000State {
uint8_t curpag;
uint8_t mult[8]; /* multicast mask array */
int irq;
+ int tainted;
PCIDevice *pci_dev;
VLANClientState *vc;
uint8_t macaddr[6];
@@ -226,6 +227,27 @@ static int ne2000_can_receive(void *opaque)
#define MIN_BUF_SIZE 60
+static inline int ne2000_valid_ring_addr(NE2000State *s, unsigned int addr)
+{
+ addr <<= 8;
+ return addr < s->stop && addr >= s->start;
+}
+
+static inline int ne2000_check_state(NE2000State *s)
+{
+ if (!s->tainted)
+ return 0;
+
+ if (s->start >= s->stop || s->stop > NE2000_MEM_SIZE)
+ return -EINVAL;
+
+ if (!ne2000_valid_ring_addr(s, s->curpag))
+ return -EINVAL;
+
+ s->tainted = 0;
+ return 0;
+}
+
static void ne2000_receive(void *opaque, const uint8_t *buf, int size)
{
NE2000State *s = opaque;
@@ -239,6 +261,12 @@ static void ne2000_receive(void *opaque, const uint8_t *buf, int size)
printf("NE2000: received len=%d\n", size);
#endif
+ if (ne2000_check_state(s))
+ return;
+
+ if (!ne2000_valid_ring_addr(s, s->boundary))
+ return;
+
if (s->cmd & E8390_STOP || ne2000_buffer_full(s))
return;
@@ -359,9 +387,11 @@ static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val)
switch(offset) {
case EN0_STARTPG:
s->start = val << 8;
+ s->tainted = 1;
break;
case EN0_STOPPG:
s->stop = val << 8;
+ s->tainted = 1;
break;
case EN0_BOUNDARY:
s->boundary = val;
@@ -406,6 +436,7 @@ static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val)
break;
case EN1_CURPAG:
s->curpag = val;
+ s->tainted = 1;
break;
case EN1_MULT ... EN1_MULT + 7:
s->mult[offset - EN1_MULT] = val;
@@ -509,7 +540,7 @@ static inline void ne2000_mem_writel(NE2000State *s, uint32_t addr,
{
addr &= ~1; /* XXX: check exact behaviour if not even */
if (addr < 32 ||
- (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+ (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE - 2)) {
cpu_to_le32wu((uint32_t *)(s->mem + addr), val);
}
}
@@ -539,7 +570,7 @@ static inline uint32_t ne2000_mem_readl(NE2000State *s, uint32_t addr)
{
addr &= ~1; /* XXX: check exact behaviour if not even */
if (addr < 32 ||
- (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+ (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE - 2)) {
return le32_to_cpupu((uint32_t *)(s->mem + addr));
} else {
return 0xffffffff;
diff --git a/tools/ioemu/hw/pc.c b/tools/ioemu/hw/pc.c
index 5b5fd42abf..234c4722bb 100644
--- a/tools/ioemu/hw/pc.c
+++ b/tools/ioemu/hw/pc.c
@@ -875,6 +875,9 @@ static void pc_init1(uint64_t ram_size, int vga_ram_size, char *boot_device,
}
}
+ if (has_tpm_device())
+ tpm_tis_init(&pic_set_irq_new, isa_pic, 11);
+
kbd_init();
DMA_init(0);
#ifdef HAS_AUDIO
diff --git a/tools/ioemu/hw/serial.c b/tools/ioemu/hw/serial.c
index 1db3e5211f..c27a803a6e 100644
--- a/tools/ioemu/hw/serial.c
+++ b/tools/ioemu/hw/serial.c
@@ -93,6 +93,15 @@ struct SerialState {
int last_break_enable;
target_ulong base;
int it_shift;
+
+ /*
+ * If a character transmitted via UART cannot be written to its
+ * destination immediately we remember it here and retry a few times via
+ * a polling timer.
+ */
+ int write_retries;
+ char write_chr;
+ QEMUTimer *write_retry_timer;
};
static void serial_update_irq(SerialState *s)
@@ -204,10 +213,32 @@ static void serial_get_token(void)
tokens_avail--;
}
+static void serial_chr_write(void *opaque)
+{
+ SerialState *s = opaque;
+
+ qemu_del_timer(s->write_retry_timer);
+
+ /* Retry every 100ms for 300ms total. */
+ if (qemu_chr_write(s->chr, &s->write_chr, 1) == -1) {
+ if (s->write_retries++ >= 3)
+ printf("serial: write error\n");
+ else
+ qemu_mod_timer(s->write_retry_timer,
+ qemu_get_clock(vm_clock) + ticks_per_sec / 10);
+ return;
+ }
+
+ /* Success: Notify guest that THR is empty. */
+ s->thr_ipending = 1;
+ s->lsr |= UART_LSR_THRE;
+ s->lsr |= UART_LSR_TEMT;
+ serial_update_irq(s);
+}
+
static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val)
{
SerialState *s = opaque;
- unsigned char ch;
addr &= 7;
#ifdef DEBUG_SERIAL
@@ -223,12 +254,9 @@ static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val)
s->thr_ipending = 0;
s->lsr &= ~UART_LSR_THRE;
serial_update_irq(s);
- ch = val;
- qemu_chr_write(s->chr, &ch, 1);
- s->thr_ipending = 1;
- s->lsr |= UART_LSR_THRE;
- s->lsr |= UART_LSR_TEMT;
- serial_update_irq(s);
+ s->write_chr = val;
+ s->write_retries = 0;
+ serial_chr_write(s);
}
break;
case 1:
@@ -424,6 +452,7 @@ SerialState *serial_init(SetIRQFunc *set_irq, void *opaque,
s->lsr = UART_LSR_TEMT | UART_LSR_THRE;
s->iir = UART_IIR_NO_INT;
s->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS;
+ s->write_retry_timer = qemu_new_timer(vm_clock, serial_chr_write, s);
register_savevm("serial", base, 1, serial_save, serial_load, s);
@@ -511,6 +540,7 @@ SerialState *serial_mm_init (SetIRQFunc *set_irq, void *opaque,
s->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS;
s->base = base;
s->it_shift = it_shift;
+ s->write_retry_timer = qemu_new_timer(vm_clock, serial_chr_write, s);
register_savevm("serial", base, 1, serial_save, serial_load, s);
diff --git a/tools/ioemu/hw/tpm_tis.c b/tools/ioemu/hw/tpm_tis.c
new file mode 100644
index 0000000000..a4d07bc67f
--- /dev/null
+++ b/tools/ioemu/hw/tpm_tis.c
@@ -0,0 +1,1114 @@
+/*
+ * tpm_tis.c - QEMU emulator for a 1.2 TPM with TIS interface
+ *
+ * Copyright (C) 2006 IBM Corporation
+ *
+ * Author: Stefan Berger <stefanb@us.ibm.com>
+ * David Safford <safford@us.ibm.com>
+ *
+ * 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.
+ *
+ *
+ * Implementation of the TIS interface according to specs at
+ * https://www.trustedcomputinggroup.org/groups/pc_client/TCG_PCClientTPMSpecification_1-20_1-00_FINAL.pdf
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "vl.h"
+
+//#define DEBUG_TPM
+
+#define TPM_MAX_PKT 4096
+
+#define VTPM_BAD_INSTANCE (uint32_t)0xffffffff
+
+#define TIS_ADDR_BASE 0xFED40000
+
+/* tis registers */
+#define TPM_REG_ACCESS 0x00
+#define TPM_REG_INT_ENABLE 0x08
+#define TPM_REG_INT_VECTOR 0x0c
+#define TPM_REG_INT_STATUS 0x10
+#define TPM_REG_INTF_CAPABILITY 0x14
+#define TPM_REG_STS 0x18
+#define TPM_REG_DATA_FIFO 0x24
+#define TPM_REG_DID_VID 0xf00
+#define TPM_REG_RID 0xf04
+
+#define STS_VALID (1 << 7)
+#define STS_COMMAND_READY (1 << 6)
+#define STS_TPM_GO (1 << 5)
+#define STS_DATA_AVAILABLE (1 << 4)
+#define STS_EXPECT (1 << 3)
+#define STS_RESPONSE_RETRY (1 << 1)
+
+#define ACCESS_TPM_REG_VALID_STS (1 << 7)
+#define ACCESS_ACTIVE_LOCALITY (1 << 5)
+#define ACCESS_BEEN_SEIZED (1 << 4)
+#define ACCESS_SEIZE (1 << 3)
+#define ACCESS_PENDING_REQUEST (1 << 2)
+#define ACCESS_REQUEST_USE (1 << 1)
+#define ACCESS_TPM_ESTABLISHMENT (1 << 0)
+
+#define INT_ENABLED (1 << 31)
+#define INT_DATA_AVAILABLE (1 << 0)
+#define INT_LOCALITY_CHANGED (1 << 2)
+#define INT_COMMAND_READY (1 << 7)
+
+#define INTERRUPTS_SUPPORTED (INT_LOCALITY_CHANGED | \
+ INT_DATA_AVAILABLE | \
+ INT_COMMAND_READY)
+#define CAPABILITIES_SUPPORTED ((1 << 4) | \
+ INTERRUPTS_SUPPORTED)
+
+enum {
+ STATE_IDLE = 0,
+ STATE_READY,
+ STATE_COMPLETION,
+ STATE_EXECUTION,
+ STATE_RECEPTION
+};
+
+#define NUM_LOCALITIES 5
+#define NO_LOCALITY 0xff
+
+#define IS_VALID_LOC(x) ((x) < NUM_LOCALITIES)
+
+#define TPM_DID 0x0001
+#define TPM_VID 0x0001
+#define TPM_RID 0x0001
+
+/* if the connection to the vTPM should be closed after a successfully
+ received response; set to '0' to allow keeping the connection */
+#define FORCE_CLOSE 0
+
+/* local data structures */
+
+typedef struct TPMTx {
+ int fd[2];
+} tpmTx;
+
+typedef struct TPMBuffer {
+ uint8_t instance[4]; /* instance number in network byte order */
+ uint8_t buf[TPM_MAX_PKT];
+} __attribute__((packed)) tpmBuffer;
+
+/* locality data */
+typedef struct TPMLocal {
+ uint32_t state;
+ uint8_t access;
+ uint8_t sts;
+ uint32_t inte;
+ uint32_t ints;
+} tpmLoc;
+
+/* overall state of the TPM interface; 's' marks as save upon suspension */
+typedef struct TPMState {
+ uint32_t offset; /* s */
+ tpmBuffer buffer; /* s */
+ uint8_t active_loc; /* s */
+ uint8_t aborting_locty;
+ uint8_t next_locty;
+ uint8_t irq_pending; /* s */
+ tpmLoc loc[NUM_LOCALITIES]; /* s */
+ QEMUTimer *poll_timer;
+ SetIRQFunc *set_irq;
+ void *irq_opaque;
+ int irq;
+ int poll_attempts;
+ uint32_t vtpm_instance; /* vtpm inst. number; determined from xenstore*/
+ int Transmitlayer;
+ tpmTx tpmTx;
+} tpmState;
+
+
+/* local prototypes */
+static int TPM_Send(tpmState *s, tpmBuffer *buffer, char *msg);
+static int TPM_Receive(tpmState *s, tpmBuffer *buffer);
+static uint32_t vtpm_instance_from_xenstore(void);
+static void tis_poll_timer(void *opaque);
+static void tis_prep_next_interrupt(tpmState *s);
+static void tis_raise_irq(tpmState *s, uint8_t locty, uint32_t irqmask);
+static void close_vtpm_channel(tpmState *s, int force);
+static void open_vtpm_channel(tpmState *s);
+static void tis_attempt_receive(tpmState *s, uint8_t locty);
+
+/* transport layer functions: local sockets */
+static int create_local_socket(tpmState *s, uint32_t vtpm_instance);
+static int write_local_socket(tpmState *s, const tpmBuffer *);
+static int read_local_socket(tpmState *s, tpmBuffer *);
+static int close_local_socket(tpmState *s, int force);
+static int has_channel_local_socket(tpmState *s);
+#define LOCAL_SOCKET_PATH "/var/vtpm/vtpm_all.socket"
+
+
+#define NUM_TRANSPORTS 1
+
+struct vTPM_transmit {
+ int (*open) (tpmState *s, uint32_t vtpm_instance);
+ int (*write) (tpmState *s, const tpmBuffer *);
+ int (*read) (tpmState *s, tpmBuffer *);
+ int (*close) (tpmState *s, int);
+ int (*has_channel) (tpmState *s);
+} vTPMTransmit[NUM_TRANSPORTS] = {
+ { .open = create_local_socket,
+ .write = write_local_socket,
+ .read = read_local_socket,
+ .close = close_local_socket,
+ .has_channel = has_channel_local_socket,
+ }
+};
+
+
+#define IS_COMM_WITH_VTPM(s) \
+ ((s)->Transmitlayer >= 0 && \
+ vTPMTransmit[(s)->Transmitlayer].has_channel(s))
+
+
+/**********************************************************************
+ helper functions
+ *********************************************************************/
+
+static inline uint32_t tpm_get_size_from_buffer(const uint8_t *buffer)
+{
+ uint32_t len = (buffer[4] << 8) + buffer[5];
+ return len;
+}
+
+static inline void tpm_initialize_instance(tpmState *s, uint32_t instance)
+{
+ s->buffer.instance[0] = (instance >> 24) & 0xff;
+ s->buffer.instance[1] = (instance >> 16) & 0xff;
+ s->buffer.instance[2] = (instance >> 8) & 0xff;
+ s->buffer.instance[3] = (instance >> 0) & 0xff;
+}
+
+/*
+ * open communication channel with a vTPM
+ */
+static void open_vtpm_channel(tpmState *s)
+{
+ int idx;
+ /* search a usable transmit layer */
+ for (idx = 0; idx < NUM_TRANSPORTS; idx++) {
+ if (1 == vTPMTransmit[idx].open(s, s->vtpm_instance)) {
+ /* found one */
+ s->Transmitlayer = idx;
+ break;
+ }
+ }
+}
+
+/*
+ * close the communication channel with the vTPM
+ */
+static inline void close_vtpm_channel(tpmState *s, int force)
+{
+ if (1 == vTPMTransmit[s->Transmitlayer].close(s, force)) {
+ s->Transmitlayer = -1;
+ }
+}
+
+static inline uint8_t locality_from_addr(target_phys_addr_t addr)
+{
+ return (uint8_t)((addr >> 12) & 0x7);
+}
+
+
+/**********************************************************************
+ low-level transmission layer methods
+ *********************************************************************/
+
+/*
+ * the 'open' method that creates the filedescriptor for communicating
+ * only one is needed for reading and writing
+ */
+static int create_local_socket(tpmState *s, uint32_t vtpm_instance)
+{
+ int success = 1;
+ if (s->tpmTx.fd[0] < 0) {
+ s->tpmTx.fd[0] = socket(PF_LOCAL, SOCK_STREAM, 0);
+
+ if (has_channel_local_socket(s)) {
+ struct sockaddr_un addr;
+ memset(&addr, 0x0, sizeof(addr));
+ addr.sun_family = AF_LOCAL;
+ strcpy(addr.sun_path, LOCAL_SOCKET_PATH);
+ if (connect(s->tpmTx.fd[0],
+ (struct sockaddr *)&addr,
+ sizeof(addr)) != 0) {
+ close_local_socket(s, 1);
+ success = 0;
+ } else {
+ /* put filedescriptor in non-blocking mode for polling */
+ int flags = fcntl(s->tpmTx.fd[0], F_GETFL);
+ fcntl(s->tpmTx.fd[0], F_SETFL, flags | O_NONBLOCK);
+ }
+#ifdef DEBUG_TPM
+ if (success)
+ fprintf(logfile,"Successfully connected using local socket "
+ LOCAL_SOCKET_PATH ".\n");
+ else
+ fprintf(logfile,"Could not connect to local socket "
+ LOCAL_SOCKET_PATH ".\n");
+#endif
+ } else {
+ success = 0;
+ }
+ }
+ return success;
+}
+
+/*
+ * the 'write' method for sending requests to the vTPM
+ * four bytes with the vTPM instance number are prepended to each request
+ */
+static int write_local_socket(tpmState *s, const tpmBuffer *buffer)
+{
+ uint32_t size = tpm_get_size_from_buffer(buffer->buf);
+ int len;
+
+ len = write(s->tpmTx.fd[0],
+ buffer->instance,
+ sizeof(buffer->instance) + size);
+ if (len == sizeof(buffer->instance) + size) {
+ return len;
+ } else {
+ return -1;
+ }
+}
+
+/*
+ * the 'read' method for receiving of responses from the TPM
+ * this function expects that four bytes with the instance number
+ * are received from the vTPM
+ */
+static int read_local_socket(tpmState *s, tpmBuffer *buffer)
+{
+ int off;
+#ifdef DEBUG_TPM
+ fprintf(logfile, "Reading from fd %d\n", s->tpmTx.fd[0]);
+#endif
+ off = read(s->tpmTx.fd[0],
+ buffer->instance,
+ sizeof(buffer->instance)+TPM_MAX_PKT);
+#ifdef DEBUG_TPM
+ fprintf(logfile, "Read %d bytes\n", off);
+#endif
+ return off;
+}
+
+/*
+ * the 'close' method
+ * shut down communication with the vTPM
+ * 'force' = 1 indicates that the socket *must* be closed
+ * 'force' = 0 indicates that a connection may be maintained
+ */
+static int close_local_socket(tpmState *s, int force)
+{
+ if (force) {
+ close(s->tpmTx.fd[0]);
+#ifdef DEBUG_TPM
+ fprintf(logfile,"Closed connection with fd %d\n",s->tpmTx.fd[0]);
+#endif
+ s->tpmTx.fd[0] = -1;
+ return 1; /* socket was closed */
+ }
+#ifdef DEBUG_TPM
+ fprintf(logfile,"Keeping connection with fd %d\n",s->tpmTx.fd[0]);
+#endif
+ return 0;
+}
+
+/*
+ * the 'has_channel' method that checks whether there's a communication
+ * channel with the vTPM
+ */
+static int has_channel_local_socket(tpmState *s)
+{
+ return (s->tpmTx.fd[0] > 0);
+}
+
+/**********************************************************************/
+
+/*
+ * read a byte of response data
+ */
+static uint32_t tpm_data_read(tpmState *s, uint8_t locty)
+{
+ uint32_t ret, len;
+
+ /* try to receive data, if none are there it is ok */
+ tis_attempt_receive(s, locty);
+
+ if (s->loc[locty].state != STATE_COMPLETION) {
+ return 0xff;
+ }
+
+ len = tpm_get_size_from_buffer(s->buffer.buf);
+ ret = s->buffer.buf[s->offset++];
+ if (s->offset >= len) {
+ s->loc[locty].sts = STS_VALID ;
+ s->offset = 0;
+ }
+#ifdef DEBUG_TPM
+ fprintf(logfile,"tpm_data_read byte x%02x [%d]\n",ret,s->offset-1);
+#endif
+ return ret;
+}
+
+
+
+/* raise an interrupt if allowed */
+static void tis_raise_irq(tpmState *s, uint8_t locty, uint32_t irqmask)
+{
+ if (!s->irq_pending &&
+ (s->loc[locty].inte & INT_ENABLED) &&
+ (s->loc[locty].inte & irqmask)) {
+ if ((irqmask & s->loc[locty].ints) == 0) {
+#ifdef DEBUG_TPM
+ fprintf(logfile,"Raising IRQ for flag %08x\n",irqmask);
+#endif
+ s->set_irq(s->irq_opaque, s->irq, 1);
+ s->irq_pending = 1;
+ s->loc[locty].ints |= irqmask;
+ }
+ }
+}
+
+/* abort execution of command */
+static void tis_abort(tpmState *s)
+{
+ s->offset = 0;
+ s->active_loc = s->next_locty;
+
+ /*
+ * Need to react differently depending on who's aborting now and
+ * which locality will become active afterwards.
+ */
+ if (s->aborting_locty == s->next_locty) {
+ s->loc[s->aborting_locty].state = STATE_READY;
+ s->loc[s->aborting_locty].sts = STS_COMMAND_READY;
+ tis_raise_irq(s, s->aborting_locty, INT_COMMAND_READY);
+ }
+
+ /* locality after abort is another one than the current one */
+ if (s->aborting_locty != s->next_locty && s->next_locty != NO_LOCALITY) {
+ s->loc[s->aborting_locty].access &= ~ACCESS_ACTIVE_LOCALITY;
+ s->loc[s->next_locty].access |= ACCESS_ACTIVE_LOCALITY;
+ tis_raise_irq(s, s->next_locty, INT_LOCALITY_CHANGED);
+ }
+
+ s->aborting_locty = NO_LOCALITY; /* nobody's aborting a command anymore */
+
+ qemu_del_timer(s->poll_timer);
+}
+
+/* abort current command */
+static void tis_prep_abort(tpmState *s, uint8_t locty, uint8_t newlocty)
+{
+ s->aborting_locty = locty; /* current locality */
+ s->next_locty = newlocty; /* locality after successful abort */
+
+ /*
+ * only abort a command using an interrupt if currently executing
+ * a command AND if there's a valid connection to the vTPM.
+ */
+ if (s->loc[locty].state == STATE_EXECUTION &&
+ IS_COMM_WITH_VTPM(s)) {
+ /* start timer and inside the timer wait for the result */
+ s->poll_attempts = 0;
+ tis_prep_next_interrupt(s);
+ } else {
+ tis_abort(s);
+ }
+}
+
+
+/*
+ * Try to receive a response from the vTPM
+ */
+static void tis_attempt_receive(tpmState *s, uint8_t locty)
+{
+ /*
+ * Attempt to read from the vTPM here if
+ * - not aborting a command
+ * - command has been sent and state is 'EXECUTION' now
+ * - no data are already available (data have already been read)
+ * - there's a communication path to the vTPM established
+ */
+ if (!IS_VALID_LOC(s->aborting_locty)) {
+ if (s->loc[locty].state == STATE_EXECUTION) {
+ if (0 == (s->loc[locty].sts & STS_DATA_AVAILABLE)){
+ if (IS_COMM_WITH_VTPM(s)) {
+ int n = TPM_Receive(s, &s->buffer);
+ if (n > 0) {
+ s->loc[locty].sts = STS_VALID | STS_DATA_AVAILABLE;
+ s->loc[locty].state = STATE_COMPLETION;
+ close_vtpm_channel(s, FORCE_CLOSE);
+ tis_raise_irq(s, locty, INT_DATA_AVAILABLE);
+ }
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Read a register of the TIS interface
+ * See specs pages 33-63 for description of the registers
+ */
+static uint32_t tis_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ tpmState *s = (tpmState *)opaque;
+ uint16_t offset = addr & 0xffc;
+ uint8_t shift = (addr & 0x3) * 8;
+ uint32_t val = 0;
+ uint8_t locty = locality_from_addr(addr);
+
+ if (offset == TPM_REG_ACCESS) {
+ if (s->active_loc == locty) {
+ s->loc[locty].access |= (1 << 5);
+ } else {
+ s->loc[locty].access &= ~(1 << 5);
+ }
+ val = s->loc[locty].access;
+ } else
+ if (offset == TPM_REG_INT_ENABLE) {
+ val = s->loc[locty].inte;
+ } else
+ if (offset == TPM_REG_INT_VECTOR) {
+ val = s->irq;
+ } else
+ if (offset == TPM_REG_INT_STATUS) {
+ tis_attempt_receive(s, locty);
+ val = s->loc[locty].ints;
+ } else
+ if (offset == TPM_REG_INTF_CAPABILITY) {
+ val = CAPABILITIES_SUPPORTED;
+ } else
+ if (offset == TPM_REG_STS) { /* status register */
+ tis_attempt_receive(s, locty);
+ val = (sizeof(s->buffer.buf) - s->offset) << 8 | s->loc[locty].sts;
+ } else
+ if (offset == TPM_REG_DATA_FIFO) {
+ val = tpm_data_read(s, locty);
+ } else
+ if (offset == TPM_REG_DID_VID) {
+ val = (TPM_DID << 16) | TPM_VID;
+ } else
+ if (offset == TPM_REG_RID) {
+ val = TPM_RID;
+ }
+
+ if (shift)
+ val >>= shift;
+
+#ifdef DEBUG_TPM
+ fprintf(logfile," read(%08x) = %08x\n",
+ addr,
+ val);
+#endif
+
+ return val;
+}
+
+/*
+ * Write a value to a register of the TIS interface
+ * See specs pages 33-63 for description of the registers
+ */
+static void tis_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ tpmState* s=(tpmState*)opaque;
+ uint16_t off = addr & 0xfff;
+ uint8_t locty = locality_from_addr(addr);
+ int n, c;
+ uint32_t len;
+
+#ifdef DEBUG_TPM
+ fprintf(logfile,"write(%08x) = %08x\n",
+ addr,
+ val);
+#endif
+
+ if (off == TPM_REG_ACCESS) {
+ if (val & ACCESS_ACTIVE_LOCALITY) {
+ /* give up locality if currently owned */
+ if (s->active_loc == locty) {
+ uint8_t newlocty = NO_LOCALITY;
+ s->loc[locty].access &= ~(ACCESS_PENDING_REQUEST);
+ /* anybody wants the locality ? */
+ for (c = NUM_LOCALITIES - 1; c >= 0; c--) {
+ if (s->loc[c].access & ACCESS_REQUEST_USE) {
+ s->loc[c].access |= ACCESS_TPM_REG_VALID_STS;
+ s->loc[c].access &= ~ACCESS_REQUEST_USE;
+ newlocty = c;
+ break;
+ }
+ }
+ tis_prep_abort(s, locty, newlocty);
+ }
+ }
+ if (val & ACCESS_BEEN_SEIZED) {
+ /* clear the flag */
+ s->loc[locty].access &= ~ACCESS_BEEN_SEIZED;
+ }
+ if (val & ACCESS_SEIZE) {
+ if (locty > s->active_loc && IS_VALID_LOC(s->active_loc)) {
+ s->loc[s->active_loc].access |= ACCESS_BEEN_SEIZED;
+ s->loc[locty].access = ACCESS_TPM_REG_VALID_STS;
+ tis_prep_abort(s, s->active_loc, locty);
+ }
+ }
+ if (val & ACCESS_REQUEST_USE) {
+ if (IS_VALID_LOC(s->active_loc)) {
+ /* locality election */
+ s->loc[s->active_loc].access |= ACCESS_PENDING_REQUEST;
+ } else {
+ /* no locality active -> make this one active now */
+ s->loc[locty].access |= ACCESS_ACTIVE_LOCALITY;
+ s->active_loc = locty;
+ tis_raise_irq(s, locty, INT_LOCALITY_CHANGED);
+ }
+ }
+ } else
+ if (off == TPM_REG_INT_ENABLE) {
+ s->loc[locty].inte = (val & (INT_ENABLED | (0x3 << 3) |
+ INTERRUPTS_SUPPORTED));
+ } else
+ if (off == TPM_REG_INT_STATUS) {
+ /* clearing of interrupt flags */
+ if ((val & INTERRUPTS_SUPPORTED) &&
+ (s->loc[locty].ints & INTERRUPTS_SUPPORTED)) {
+ s->set_irq(s->irq_opaque, s->irq, 0);
+ s->irq_pending = 0;
+ }
+ s->loc[locty].ints &= ~(val & INTERRUPTS_SUPPORTED);
+ } else
+ if (off == TPM_REG_STS) {
+ if (val & STS_COMMAND_READY) {
+ if (s->loc[locty].state == STATE_IDLE) {
+ s->loc[locty].sts = STS_COMMAND_READY;
+ s->loc[locty].state = STATE_READY;
+ tis_raise_irq(s, locty, INT_COMMAND_READY);
+ } else if (s->loc[locty].state == STATE_COMPLETION ||
+ s->loc[locty].state == STATE_EXECUTION ||
+ s->loc[locty].state == STATE_RECEPTION) {
+ /* abort currently running command */
+ tis_prep_abort(s, locty, locty);
+ }
+ }
+ if (val & STS_TPM_GO) {
+ n = TPM_Send(s, &s->buffer,"tpm_data_write");
+ if (n > 0) {
+ /* sending of data was successful */
+ s->offset = 0;
+ s->loc[locty].state = STATE_EXECUTION;
+ if (s->loc[locty].inte & (INT_ENABLED | INT_DATA_AVAILABLE)) {
+ s->poll_attempts = 0;
+ tis_prep_next_interrupt(s);
+ }
+ }
+ }
+ if (val & STS_RESPONSE_RETRY) {
+ s->offset = 0;
+ }
+ } else if (off == TPM_REG_DATA_FIFO) {
+ /* data fifo */
+ if (s->loc[locty].state == STATE_IDLE ||
+ s->loc[locty].state == STATE_EXECUTION ||
+ s->loc[locty].state == STATE_COMPLETION) {
+ /* drop the byte */
+ } else {
+#ifdef TPM_DEBUG
+ fprintf(logfile,"Byte to send to TPM: %02x\n", val);
+#endif
+ s->loc[locty].state = STATE_RECEPTION;
+
+ if (s->offset < sizeof(s->buffer.buf))
+ s->buffer.buf[s->offset++] = (uint8_t)val;
+
+ if (s->offset > 5) {
+ /* we have a packet length - see if we have all of it */
+ len = tpm_get_size_from_buffer(s->buffer.buf);
+ if (len > s->offset) {
+ s->loc[locty].sts = STS_EXPECT | STS_VALID;
+ } else {
+ s->loc[locty].sts = STS_VALID;
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Prepare the next interrupt for example after a command has
+ * been sent out for the purpose of receiving the response.
+ * Depending on how many interrupts (used for polling on the fd) have
+ * already been schedule, this function determines the delta in time
+ * to the next interrupt. This accomodates for commands that finish
+ * quickly.
+ */
+static void tis_prep_next_interrupt(tpmState *s)
+{
+ int64_t expiration;
+ int rate = 5; /* 5 times per second */
+
+ /*
+ poll often at the beginning for quickly finished commands,
+ then back off
+ */
+ if (s->poll_attempts < 5) {
+ rate = 20;
+ } else if (s->poll_attempts < 10) {
+ rate = 10;
+ }
+
+ expiration = qemu_get_clock(vm_clock) + (ticks_per_sec / rate);
+ qemu_mod_timer(s->poll_timer, expiration);
+ s->poll_attempts++;
+}
+
+
+/*
+ * The polling routine called when the 'timer interrupt' fires.
+ * Tries to receive a command from the vTPM.
+ */
+static void tis_poll_timer(void *opaque)
+{
+ tpmState* s=(tpmState*)opaque;
+ uint8_t locty = s->active_loc;
+
+ if (!IS_VALID_LOC(locty) ||
+ (!(s->loc[locty].inte & INT_ENABLED) &&
+ (s->aborting_locty != NO_LOCALITY)) ||
+ !IS_COMM_WITH_VTPM(s)) {
+ /* no more interrupts requested, so no more polling needed */
+ qemu_del_timer(s->poll_timer);
+ }
+
+ if (!IS_COMM_WITH_VTPM(s)) {
+ if (s->aborting_locty != NO_LOCALITY) {
+ tis_abort(s);
+ }
+ return;
+ }
+
+ if (s->aborting_locty != NO_LOCALITY) {
+ int n = TPM_Receive(s, &s->buffer);
+#ifdef DEBUG_TPM
+ fprintf(logfile,"Receiving for abort.\n");
+#endif
+ if (n > 0) {
+ close_vtpm_channel(s, FORCE_CLOSE);
+ tis_abort(s);
+#ifdef DEBUG_TPM
+ fprintf(logfile,"Abort is complete.\n");
+#endif
+ } else {
+ tis_prep_next_interrupt(s);
+ }
+ } else if (IS_VALID_LOC(locty)) {
+ if (s->loc[locty].state == STATE_EXECUTION) {
+ /* poll for result */
+ int n = TPM_Receive(s, &s->buffer);
+ if (n > 0) {
+ s->loc[locty].sts = STS_VALID | STS_DATA_AVAILABLE;
+ s->loc[locty].state = STATE_COMPLETION;
+ close_vtpm_channel(s, FORCE_CLOSE);
+ tis_raise_irq(s, locty, INT_DATA_AVAILABLE);
+ } else {
+ /* nothing received */
+ tis_prep_next_interrupt(s);
+ }
+ }
+ }
+}
+
+
+static CPUReadMemoryFunc *tis_readfn[3]={
+ tis_mem_readl,
+ tis_mem_readl,
+ tis_mem_readl
+};
+
+static CPUWriteMemoryFunc *tis_writefn[3]={
+ tis_mem_writel,
+ tis_mem_writel,
+ tis_mem_writel
+};
+
+/*
+ * Save the internal state of this interface for later resumption.
+ * Need to get any outstanding responses from the vTPM back, so
+ * this might delay the suspend for a while.
+ */
+static void tpm_save(QEMUFile* f,void* opaque)
+{
+ tpmState* s=(tpmState*)opaque;
+ int c;
+
+ /* need to wait for outstanding requests to complete */
+ if (IS_COMM_WITH_VTPM(s)) {
+ int repeats = 30; /* 30 seconds; really should be infty */
+ while (repeats > 0 &&
+ !(s->loc[s->active_loc].sts & STS_DATA_AVAILABLE)) {
+ int n = TPM_Receive(s, &s->buffer);
+ if (n > 0) {
+ if (IS_VALID_LOC(s->active_loc)) {
+ s->loc[s->active_loc].sts = STS_VALID | STS_DATA_AVAILABLE;
+ }
+ /* close the connection with the vTPM for good */
+ close_vtpm_channel(s, 1);
+ break;
+ }
+ sleep(1);
+ }
+ }
+
+ qemu_put_be32s(f,&s->offset);
+ qemu_put_buffer(f, s->buffer.buf, TPM_MAX_PKT);
+ qemu_put_8s(f, &s->active_loc);
+ qemu_put_8s(f, &s->irq_pending);
+ for (c = 0; c < NUM_LOCALITIES; c++) {
+ qemu_put_be32s(f, &s->loc[c].state);
+ qemu_put_8s(f, &s->loc[c].access);
+ qemu_put_8s(f, &s->loc[c].sts);
+ qemu_put_be32s(f, &s->loc[c].inte);
+ qemu_put_be32s(f, &s->loc[c].ints);
+ }
+}
+
+/*
+ * load TIS interface state
+ */
+static int tpm_load(QEMUFile* f,void* opaque,int version_id)
+{
+ tpmState* s=(tpmState*)opaque;
+ int c;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ qemu_get_be32s(f,&s->offset);
+ qemu_get_buffer(f, s->buffer.buf, TPM_MAX_PKT);
+ qemu_get_8s(f, &s->active_loc);
+ qemu_get_8s(f, &s->irq_pending);
+ for (c = 0; c < NUM_LOCALITIES; c++) {
+ qemu_get_be32s(f, &s->loc[c].state);
+ qemu_get_8s(f, &s->loc[c].access);
+ qemu_get_8s(f, &s->loc[c].sts);
+ qemu_get_be32s(f, &s->loc[c].inte);
+ qemu_get_be32s(f, &s->loc[c].ints);
+ }
+
+ /* need to be able to get the instance number from the xenstore */
+ s->vtpm_instance = vtpm_instance_from_xenstore();
+ if (s->vtpm_instance == VTPM_BAD_INSTANCE)
+ return -EINVAL;
+ tpm_initialize_instance(s, s->vtpm_instance);
+
+ return 0;
+}
+
+
+typedef struct LPCtpmState {
+ tpmState tpm;
+ int mem;
+} LPCtpmState;
+
+
+/*
+ * initialize TIS interface
+ */
+void tpm_tis_init(SetIRQFunc *set_irq, void *opaque, int irq)
+{
+ LPCtpmState *d;
+ tpmState *s;
+ int c = 0;
+ uint32_t vtpm_in;
+
+ vtpm_in = vtpm_instance_from_xenstore();
+ /* no valid vtpm instance -> no device */
+ if (vtpm_in == VTPM_BAD_INSTANCE)
+ return;
+
+ d = qemu_mallocz(sizeof(LPCtpmState));
+ d->mem = cpu_register_io_memory(0, tis_readfn, tis_writefn, d);
+
+ if (d->mem == -1) {
+ return;
+ }
+
+ cpu_register_physical_memory(TIS_ADDR_BASE,
+ 0x1000 * NUM_LOCALITIES, d->mem);
+
+ /* initialize tpmState */
+ s = &d->tpm;
+
+ s->offset = 0;
+ s->active_loc = NO_LOCALITY;
+
+ while (c < NUM_LOCALITIES) {
+ s->loc[c].access = (1 << 7);
+ s->loc[c].sts = 0;
+ s->loc[c].inte = (1 << 3);
+ s->loc[c].ints = 0;
+ s->loc[c].state = STATE_IDLE;
+ c++;
+ }
+ s->poll_timer = qemu_new_timer(vm_clock, tis_poll_timer, s);
+ s->set_irq = set_irq;
+ s->irq_opaque = opaque;
+ s->irq = irq;
+ s->vtpm_instance = vtpm_in;
+ s->Transmitlayer = -1;
+ s->tpmTx.fd[0] = -1;
+ s->tpmTx.fd[1] = -1;
+
+ tpm_initialize_instance(s, s->vtpm_instance);
+ memset(s->buffer.buf,0,sizeof(s->buffer.buf));
+
+ register_savevm("tpm-tis", 0, 1, tpm_save, tpm_load, s);
+}
+
+/****************************************************************************/
+/* optional verbose logging of data to/from vtpm */
+/****************************************************************************/
+#ifdef DEBUG_TPM
+static void showBuff(unsigned char *buff, char *string)
+{
+ uint32_t i, len;
+
+ len = tpm_get_size_from_buffer(buff);
+ fprintf(logfile,"%s length = %d\n", string, len);
+ for (i = 0; i < len; i++) {
+ if (i && !(i % 16)) {
+ fprintf(logfile,"\n");
+ }
+ fprintf(logfile,"%.2X ", buff[i]);
+ }
+ fprintf(logfile,"\n");
+}
+#endif
+
+/****************************************************************************/
+/* Transmit request to TPM and read Response */
+/****************************************************************************/
+
+const static unsigned char tpm_failure[] = {
+ 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0a,
+ 0x00, 0x00, 0x00, 0x09
+};
+
+
+/*
+ * Send a TPM request.
+ */
+static int TPM_Send(tpmState *s, tpmBuffer *buffer, char *msg)
+{
+ int len;
+ uint32_t size = tpm_get_size_from_buffer(buffer->buf);
+
+ /* try to establish a connection to the vTPM */
+ if ( !IS_COMM_WITH_VTPM(s)) {
+ open_vtpm_channel(s);
+ }
+
+ if ( !IS_COMM_WITH_VTPM(s)) {
+ unsigned char tag = buffer->buf[1];
+
+ /* there's a failure response from the TPM */
+ memcpy(buffer->buf, tpm_failure, sizeof(tpm_failure));
+ buffer->buf[1] = tag + 3;
+ if (IS_VALID_LOC(s->active_loc)) {
+ s->loc[s->active_loc].sts = STS_DATA_AVAILABLE | STS_VALID;
+ }
+#ifdef DEBUG_TPM
+ fprintf(logfile,"No TPM running!\n");
+#endif
+ /* the request went out ok. */
+ return sizeof(buffer->instance) + size;
+ }
+
+#ifdef DEBUG_TPM
+ showBuff(buffer->buf, "To TPM");
+#endif
+
+ len = vTPMTransmit[s->Transmitlayer].write(s, buffer);
+ if (len < 0) {
+ s->Transmitlayer = -1;
+ }
+ return len;
+}
+
+/*
+ * Try to receive data from the file descriptor. Since it is in
+ * non-blocking mode it is possible that no data are actually received -
+ * whatever calls this function needs to try again later.
+ */
+static int TPM_Receive(tpmState *s, tpmBuffer *buffer)
+{
+ int off;
+
+ off = vTPMTransmit[s->Transmitlayer].read(s, buffer);
+
+ if (off < 0) {
+ /* EAGAIN is set in errno due to non-blocking mode */
+ return -1;
+ }
+
+ if (off == 0) {
+#ifdef DEBUG_TPM
+ fprintf(logfile,"TPM GONE? errno=%d\n",errno);
+#endif
+ close_vtpm_channel(s, 1);
+ /* pretend that data are available */
+ if (IS_VALID_LOC(s->active_loc)) {
+ s->loc[s->active_loc].sts = STS_VALID | STS_DATA_AVAILABLE;
+ s->loc[s->active_loc].state = STATE_COMPLETION;
+ tis_raise_irq(s, s->active_loc, INT_DATA_AVAILABLE);
+ }
+ return -1;
+ }
+
+#ifdef DEBUG_TPM
+ if (off > sizeof(buffer->instance ) + 6) {
+ uint32_t size = tpm_get_size_from_buffer(buffer->buf);
+ if (size + sizeof(buffer->instance) != off) {
+ fprintf(logfile,"TPM: Packet size is bad! %d != %d\n",
+ size + sizeof(buffer->instance),
+ off);
+ } else {
+ uint32_t ret;
+ showBuff(buffer->buf, "From TPM");
+ ret = (buffer->buf[8])*256 + buffer->buf[9];
+ if (ret)
+ fprintf(logfile,"Receive failed with error %d\n", ret);
+ else
+ fprintf(logfile,"Receive succeeded. Got response of length %d (=%d)\n",
+ size, off);
+ }
+ }
+#endif
+
+ /* assuming reading in one chunk for now */
+ return off;
+}
+
+
+/****************************************************************************
+ Helper functions for reading data from the xenstore such as
+ reading virtual TPM instance information
+ ****************************************************************************/
+int has_tpm_device(void)
+{
+ int ret = 0;
+ struct xs_handle *handle = xs_daemon_open();
+ if (handle) {
+ ret = xenstore_domain_has_devtype(handle, "vtpm");
+ xs_daemon_close(handle);
+ }
+ return ret;
+}
+
+
+/*
+ * Wait until hotplug scripts have finished then read the vTPM instance
+ * number from the xenstore.
+ */
+static uint32_t vtpm_instance_from_xenstore(void)
+{
+ unsigned int num;
+ uint32_t number = VTPM_BAD_INSTANCE;
+ int end = 0;
+ char *token = "tok";
+ int subscribed = 0;
+ int ctr = 0;
+ fd_set readfds;
+
+ struct xs_handle *handle = xs_daemon_open();
+
+ FD_ZERO(&readfds);
+
+ if (handle) {
+ char **e = xenstore_domain_get_devices(handle, "vtpm", &num);
+ int fd = xs_fileno(handle);
+ FD_SET(fd, &readfds);
+ if (e) {
+ do {
+ struct timeval tv = {
+ .tv_sec = 30,
+ .tv_usec = 0,
+ };
+ /* need to make sure that the hotplug scripts have finished */
+ char *status = xenstore_read_hotplug_status(handle,
+ "vtpm",
+ e[0]);
+ if (status) {
+ if (!strcmp(status, "connected")) {
+ char *inst = xenstore_backend_read_variable(handle,
+ "vtpm",
+ e[0],
+ "instance");
+ if (1 != (sscanf(inst,"%d",&number)))
+ number = VTPM_BAD_INSTANCE;
+ free(inst);
+ } else {
+ fprintf(logfile,
+ "bad status '%s' from vtpm hotplug\n",
+ status);
+ }
+ free(status);
+ end = 1;
+ } else {
+ /* no status, yet */
+ int rc;
+ unsigned int nr;
+ char **f;
+
+ if (!subscribed) {
+ rc = xenstore_subscribe_to_hotplug_status(handle,
+ "vtpm",
+ e[0],
+ token);
+ if (rc != 0)
+ break;
+ subscribed = 1;
+ }
+ rc = select(fd+1, &readfds, NULL, NULL, &tv);
+ /* get what's available -- drain the fd */
+ f = xs_read_watch(handle, &nr);
+ ctr++;
+ free(f);
+ if (ctr > 2)
+ end = 1;
+ }
+ } while (end == 0);
+ free(e);
+ }
+ if (subscribed) {
+ /* clean up */
+ xenstore_unsubscribe_from_hotplug_status(handle,
+ "vtpm",
+ e[0],
+ token);
+ }
+ xs_daemon_close(handle);
+ }
+ if (number == VTPM_BAD_INSTANCE)
+ fprintf(logfile, "no valid vtpm instance");
+ else
+ fprintf(logfile,"vtpm instance:%d\n",number);
+ return number;
+}
diff --git a/tools/ioemu/keymaps/ja b/tools/ioemu/keymaps/ja
index 8fd0b9ef1b..770220c588 100644
--- a/tools/ioemu/keymaps/ja
+++ b/tools/ioemu/keymaps/ja
@@ -102,3 +102,6 @@ underscore 0x73 shift
Henkan_Mode 0x79
Katakana 0x70
Muhenkan 0x7b
+Henkan_Mode_Real 0x79
+Henkan_Mode_Ultra 0x79
+backslash_ja 0x73
diff --git a/tools/ioemu/target-i386-dm/cpu.h b/tools/ioemu/target-i386-dm/cpu.h
index 8bc6c7bda9..dc016edd3e 100644
--- a/tools/ioemu/target-i386-dm/cpu.h
+++ b/tools/ioemu/target-i386-dm/cpu.h
@@ -55,8 +55,6 @@ typedef struct CPUX86State {
int interrupt_request;
CPU_COMMON
-
- int send_event;
} CPUX86State;
CPUX86State *cpu_x86_init(void);
diff --git a/tools/ioemu/target-i386-dm/exec-dm.c b/tools/ioemu/target-i386-dm/exec-dm.c
index cd7af5583f..be8b280dda 100644
--- a/tools/ioemu/target-i386-dm/exec-dm.c
+++ b/tools/ioemu/target-i386-dm/exec-dm.c
@@ -32,6 +32,8 @@
#include <unistd.h>
#include <inttypes.h>
+#include <xen/hvm/e820.h>
+
#include "cpu.h"
#include "exec-all.h"
@@ -407,22 +409,36 @@ int iomem_index(target_phys_addr_t addr)
return 0;
}
+static inline int paddr_is_ram(target_phys_addr_t addr)
+{
+ /* Is this guest physical address RAM-backed? */
+#if defined(CONFIG_DM) && (defined(__i386__) || defined(__x86_64__))
+ if (ram_size <= HVM_BELOW_4G_RAM_END)
+ /* RAM is contiguous */
+ return (addr < ram_size);
+ else
+ /* There is RAM below and above the MMIO hole */
+ return ((addr < HVM_BELOW_4G_MMIO_START) ||
+ ((addr >= HVM_BELOW_4G_MMIO_START + HVM_BELOW_4G_MMIO_LENGTH)
+ && (addr < ram_size + HVM_BELOW_4G_MMIO_LENGTH)));
+#else
+ return (addr < ram_size);
+#endif
+}
+
void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf,
int len, int is_write)
{
int l, io_index;
uint8_t *ptr;
uint32_t val;
- target_phys_addr_t page;
- unsigned long pd;
while (len > 0) {
- page = addr & TARGET_PAGE_MASK;
- l = (page + TARGET_PAGE_SIZE) - addr;
+ /* How much can we copy before the next page boundary? */
+ l = TARGET_PAGE_SIZE - (addr & ~TARGET_PAGE_MASK);
if (l > len)
l = len;
- pd = page;
io_index = iomem_index(addr);
if (is_write) {
if (io_index) {
@@ -442,15 +458,11 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf,
io_mem_write[io_index][0](io_mem_opaque[io_index], addr, val);
l = 1;
}
- } else {
- unsigned long addr1;
-
- addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK);
- /* RAM case */
- ptr = phys_ram_base + addr1;
- memcpy(ptr, buf, l);
+ } else if (paddr_is_ram(addr)) {
+ /* Reading from RAM */
+ memcpy(phys_ram_base + addr, buf, l);
#ifdef __ia64__
- sync_icache((unsigned long)ptr, l);
+ sync_icache((unsigned long)(phys_ram_base + addr), l);
#endif
}
} else {
@@ -471,14 +483,12 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf,
stb_raw(buf, val);
l = 1;
}
- } else if (addr < ram_size) {
- /* RAM case */
- ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) +
- (addr & ~TARGET_PAGE_MASK);
- memcpy(buf, ptr, l);
+ } else if (paddr_is_ram(addr)) {
+ /* Reading from RAM */
+ memcpy(buf, phys_ram_base + addr, l);
} else {
- /* unreported MMIO space */
- memset(buf, 0xff, len);
+ /* Neither RAM nor known MMIO space */
+ memset(buf, 0xff, len);
}
}
len -= l;
diff --git a/tools/ioemu/target-i386-dm/helper2.c b/tools/ioemu/target-i386-dm/helper2.c
index 83fc5e8f00..aa7b042561 100644
--- a/tools/ioemu/target-i386-dm/helper2.c
+++ b/tools/ioemu/target-i386-dm/helper2.c
@@ -193,10 +193,10 @@ void sp_info()
for (i = 0; i < vcpus; i++) {
req = &(shared_page->vcpu_iodata[i].vp_ioreq);
term_printf("vcpu %d: event port %d\n", i, ioreq_local_port[i]);
- term_printf(" req state: %x, pvalid: %x, addr: %"PRIx64", "
+ term_printf(" req state: %x, ptr: %x, addr: %"PRIx64", "
"data: %"PRIx64", count: %"PRIx64", size: %"PRIx64"\n",
- req->state, req->pdata_valid, req->addr,
- req->u.data, req->count, req->size);
+ req->state, req->data_is_ptr, req->addr,
+ req->data, req->count, req->size);
term_printf(" IO totally occurred on this vcpu: %"PRIx64"\n",
req->io_count);
}
@@ -209,18 +209,19 @@ static ioreq_t *__cpu_get_ioreq(int vcpu)
req = &(shared_page->vcpu_iodata[vcpu].vp_ioreq);
- if (req->state == STATE_IOREQ_READY) {
- req->state = STATE_IOREQ_INPROCESS;
- rmb();
- return req;
+ if (req->state != STATE_IOREQ_READY) {
+ fprintf(logfile, "I/O request not ready: "
+ "%x, ptr: %x, port: %"PRIx64", "
+ "data: %"PRIx64", count: %"PRIx64", size: %"PRIx64"\n",
+ req->state, req->data_is_ptr, req->addr,
+ req->data, req->count, req->size);
+ return NULL;
}
- fprintf(logfile, "False I/O request ... in-service already: "
- "%x, pvalid: %x, port: %"PRIx64", "
- "data: %"PRIx64", count: %"PRIx64", size: %"PRIx64"\n",
- req->state, req->pdata_valid, req->addr,
- req->u.data, req->count, req->size);
- return NULL;
+ rmb(); /* see IOREQ_READY /then/ read contents of ioreq */
+
+ req->state = STATE_IOREQ_INPROCESS;
+ return req;
}
//use poll to get the port notification
@@ -305,26 +306,26 @@ void cpu_ioreq_pio(CPUState *env, ioreq_t *req)
sign = req->df ? -1 : 1;
if (req->dir == IOREQ_READ) {
- if (!req->pdata_valid) {
- req->u.data = do_inp(env, req->addr, req->size);
+ if (!req->data_is_ptr) {
+ req->data = do_inp(env, req->addr, req->size);
} else {
unsigned long tmp;
for (i = 0; i < req->count; i++) {
tmp = do_inp(env, req->addr, req->size);
- write_physical((target_phys_addr_t) req->u.pdata
+ write_physical((target_phys_addr_t) req->data
+ (sign * i * req->size),
req->size, &tmp);
}
}
} else if (req->dir == IOREQ_WRITE) {
- if (!req->pdata_valid) {
- do_outp(env, req->addr, req->size, req->u.data);
+ if (!req->data_is_ptr) {
+ do_outp(env, req->addr, req->size, req->data);
} else {
for (i = 0; i < req->count; i++) {
unsigned long tmp;
- read_physical((target_phys_addr_t) req->u.pdata
+ read_physical((target_phys_addr_t) req->data
+ (sign * i * req->size),
req->size, &tmp);
do_outp(env, req->addr, req->size, tmp);
@@ -339,18 +340,18 @@ void cpu_ioreq_move(CPUState *env, ioreq_t *req)
sign = req->df ? -1 : 1;
- if (!req->pdata_valid) {
+ if (!req->data_is_ptr) {
if (req->dir == IOREQ_READ) {
for (i = 0; i < req->count; i++) {
read_physical(req->addr
+ (sign * i * req->size),
- req->size, &req->u.data);
+ req->size, &req->data);
}
} else if (req->dir == IOREQ_WRITE) {
for (i = 0; i < req->count; i++) {
write_physical(req->addr
+ (sign * i * req->size),
- req->size, &req->u.data);
+ req->size, &req->data);
}
}
} else {
@@ -361,13 +362,13 @@ void cpu_ioreq_move(CPUState *env, ioreq_t *req)
read_physical(req->addr
+ (sign * i * req->size),
req->size, &tmp);
- write_physical((target_phys_addr_t )req->u.pdata
+ write_physical((target_phys_addr_t )req->data
+ (sign * i * req->size),
req->size, &tmp);
}
} else if (req->dir == IOREQ_WRITE) {
for (i = 0; i < req->count; i++) {
- read_physical((target_phys_addr_t) req->u.pdata
+ read_physical((target_phys_addr_t) req->data
+ (sign * i * req->size),
req->size, &tmp);
write_physical(req->addr
@@ -382,51 +383,66 @@ void cpu_ioreq_and(CPUState *env, ioreq_t *req)
{
unsigned long tmp1, tmp2;
- if (req->pdata_valid != 0)
+ if (req->data_is_ptr != 0)
+ hw_error("expected scalar value");
+
+ read_physical(req->addr, req->size, &tmp1);
+ if (req->dir == IOREQ_WRITE) {
+ tmp2 = tmp1 & (unsigned long) req->data;
+ write_physical(req->addr, req->size, &tmp2);
+ }
+ req->data = tmp1;
+}
+
+void cpu_ioreq_add(CPUState *env, ioreq_t *req)
+{
+ unsigned long tmp1, tmp2;
+
+ if (req->data_is_ptr != 0)
hw_error("expected scalar value");
read_physical(req->addr, req->size, &tmp1);
if (req->dir == IOREQ_WRITE) {
- tmp2 = tmp1 & (unsigned long) req->u.data;
+ tmp2 = tmp1 + (unsigned long) req->data;
write_physical(req->addr, req->size, &tmp2);
}
- req->u.data = tmp1;
+ req->data = tmp1;
}
void cpu_ioreq_or(CPUState *env, ioreq_t *req)
{
unsigned long tmp1, tmp2;
- if (req->pdata_valid != 0)
+ if (req->data_is_ptr != 0)
hw_error("expected scalar value");
read_physical(req->addr, req->size, &tmp1);
if (req->dir == IOREQ_WRITE) {
- tmp2 = tmp1 | (unsigned long) req->u.data;
+ tmp2 = tmp1 | (unsigned long) req->data;
write_physical(req->addr, req->size, &tmp2);
}
- req->u.data = tmp1;
+ req->data = tmp1;
}
void cpu_ioreq_xor(CPUState *env, ioreq_t *req)
{
unsigned long tmp1, tmp2;
- if (req->pdata_valid != 0)
+ if (req->data_is_ptr != 0)
hw_error("expected scalar value");
read_physical(req->addr, req->size, &tmp1);
if (req->dir == IOREQ_WRITE) {
- tmp2 = tmp1 ^ (unsigned long) req->u.data;
+ tmp2 = tmp1 ^ (unsigned long) req->data;
write_physical(req->addr, req->size, &tmp2);
}
- req->u.data = tmp1;
+ req->data = tmp1;
}
void __handle_ioreq(CPUState *env, ioreq_t *req)
{
- if (!req->pdata_valid && req->dir == IOREQ_WRITE && req->size != 4)
- req->u.data &= (1UL << (8 * req->size)) - 1;
+ if (!req->data_is_ptr && req->dir == IOREQ_WRITE && req->size != 4)
+ req->data &= (1UL << (8 * req->size)) - 1;
switch (req->type) {
case IOREQ_TYPE_PIO:
@@ -438,6 +454,9 @@ void __handle_ioreq(CPUState *env, ioreq_t *req)
case IOREQ_TYPE_AND:
cpu_ioreq_and(env, req);
break;
+ case IOREQ_TYPE_ADD:
+ cpu_ioreq_add(env, req);
+ break;
case IOREQ_TYPE_OR:
cpu_ioreq_or(env, req);
break;
@@ -486,12 +505,19 @@ void cpu_handle_ioreq(void *opaque)
if (req) {
__handle_ioreq(env, req);
- /* No state change if state = STATE_IORESP_HOOK */
- if (req->state == STATE_IOREQ_INPROCESS) {
- mb();
- req->state = STATE_IORESP_READY;
+ if (req->state != STATE_IOREQ_INPROCESS) {
+ fprintf(logfile, "Badness in I/O request ... not in service?!: "
+ "%x, ptr: %x, port: %"PRIx64", "
+ "data: %"PRIx64", count: %"PRIx64", size: %"PRIx64"\n",
+ req->state, req->data_is_ptr, req->addr,
+ req->data, req->count, req->size);
+ destroy_hvm_domain();
+ return;
}
- env->send_event = 1;
+
+ wmb(); /* Update ioreq contents /then/ update state. */
+ req->state = STATE_IORESP_READY;
+ xc_evtchn_notify(xce_handle, ioreq_local_port[send_vcpu]);
}
}
@@ -508,8 +534,6 @@ int main_loop(void)
qemu_set_fd_handler(evtchn_fd, cpu_handle_ioreq, NULL, env);
- env->send_event = 0;
-
while (1) {
if (vm_running) {
if (shutdown_requested)
@@ -522,11 +546,6 @@ int main_loop(void)
/* Wait up to 10 msec. */
main_loop_wait(10);
-
- if (env->send_event) {
- env->send_event = 0;
- xc_evtchn_notify(xce_handle, ioreq_local_port[send_vcpu]);
- }
}
destroy_hvm_domain();
return 0;
diff --git a/tools/ioemu/target-i386-dm/i8259-dm.c b/tools/ioemu/target-i386-dm/i8259-dm.c
index 3243b63b5d..208609a010 100644
--- a/tools/ioemu/target-i386-dm/i8259-dm.c
+++ b/tools/ioemu/target-i386-dm/i8259-dm.c
@@ -22,58 +22,18 @@
* THE SOFTWARE.
*/
#include "vl.h"
-
-/* debug PIC */
-//#define DEBUG_PIC
-
-//#define DEBUG_IRQ_LATENCY
-//#define DEBUG_IRQ_COUNT
-
#include "xenctrl.h"
#include <xen/hvm/ioreq.h>
#include <stdio.h>
#include "cpu.h"
#include "cpu-all.h"
-extern shared_iopage_t *shared_page;
-
struct PicState2 {
};
void pic_set_irq_new(void *opaque, int irq, int level)
{
- /* PicState2 *s = opaque; */
- global_iodata_t *gio;
- int mask;
-
- gio = &shared_page->sp_global;
- mask = 1 << irq;
- if ( gio->pic_elcr & mask ) {
- /* level */
- if ( level ) {
- atomic_clear_bit(irq, &gio->pic_clear_irr);
- atomic_set_bit(irq, &gio->pic_irr);
- cpu_single_env->send_event = 1;
- }
- else {
- atomic_clear_bit(irq, &gio->pic_irr);
- atomic_set_bit(irq, &gio->pic_clear_irr);
- cpu_single_env->send_event = 1;
- }
- }
- else {
- /* edge */
- if ( level ) {
- if ( (mask & gio->pic_last_irr) == 0 ) {
- atomic_set_bit(irq, &gio->pic_irr);
- atomic_set_bit(irq, &gio->pic_last_irr);
- cpu_single_env->send_event = 1;
- }
- }
- else {
- atomic_clear_bit(irq, &gio->pic_last_irr);
- }
- }
+ xc_hvm_set_irq_level(xc_handle, domid, irq, level);
}
/* obsolete function */
diff --git a/tools/ioemu/target-i386-dm/qemu-dm.debug b/tools/ioemu/target-i386-dm/qemu-dm.debug
index 3bad6d900d..cea6b57c77 100644
--- a/tools/ioemu/target-i386-dm/qemu-dm.debug
+++ b/tools/ioemu/target-i386-dm/qemu-dm.debug
@@ -1,5 +1,10 @@
#!/bin/sh
+if [ "`arch`" = "x86_64" ]; then
+ LIBDIR="lib64"
+else
+ LIBDIR="lib"
+fi
echo $* > /tmp/args
echo $DISPLAY >> /tmp/args
-exec /usr/lib/xen/bin/qemu-dm $*
+exec /usr/$LIBDIR/xen/bin/qemu-dm $*
diff --git a/tools/ioemu/vl.c b/tools/ioemu/vl.c
index e331abd1ae..924aa3c999 100644
--- a/tools/ioemu/vl.c
+++ b/tools/ioemu/vl.c
@@ -1684,7 +1684,7 @@ static void tty_serial_init(int fd, int speed,
tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
|INLCR|IGNCR|ICRNL|IXON);
- tty.c_oflag |= OPOST;
+ tty.c_oflag &= ~OPOST; /* no output mangling of raw serial stream */
tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN|ISIG);
tty.c_cflag &= ~(CSIZE|PARENB|PARODD|CRTSCTS);
switch(data_bits) {
@@ -6390,6 +6390,11 @@ int main(int argc, char **argv)
exit(1);
}
+#if defined (__ia64__)
+ if (ram_size > MMIO_START)
+ ram_size += 1 * MEM_G; /* skip 3G-4G MMIO, LEGACY_IO_SPACE etc. */
+#endif
+
/* init the memory */
phys_ram_size = ram_size + vga_ram_size + bios_size;
@@ -6397,11 +6402,6 @@ int main(int argc, char **argv)
xc_handle = xc_interface_open();
-#if defined (__ia64__)
- if (ram_size > MMIO_START)
- ram_size += 1 * MEM_G; /* skip 3G-4G MMIO, LEGACY_IO_SPACE etc. */
-#endif
-
nr_pages = ram_size/PAGE_SIZE;
tmp_nr_pages = nr_pages;
@@ -6420,15 +6420,14 @@ int main(int argc, char **argv)
}
#if defined(__i386__) || defined(__x86_64__)
- if (xc_get_pfn_list(xc_handle, domid, page_array, nr_pages) != nr_pages) {
+ for ( i = 0; i < tmp_nr_pages; i++)
+ page_array[i] = i;
+ if (xc_domain_translate_gpfn_list(xc_handle, domid, tmp_nr_pages,
+ page_array, page_array)) {
fprintf(logfile, "xc_get_pfn_list returned error %d\n", errno);
exit(-1);
}
- if (ram_size > HVM_BELOW_4G_RAM_END)
- for (i = 0; i < nr_pages - (HVM_BELOW_4G_RAM_END >> PAGE_SHIFT); i++)
- page_array[tmp_nr_pages - 1 - i] = page_array[nr_pages - 1 - i];
-
phys_ram_base = xc_map_foreign_batch(xc_handle, domid,
PROT_READ|PROT_WRITE, page_array,
tmp_nr_pages);
diff --git a/tools/ioemu/vl.h b/tools/ioemu/vl.h
index 864079f682..4ff660c217 100644
--- a/tools/ioemu/vl.h
+++ b/tools/ioemu/vl.h
@@ -929,6 +929,10 @@ extern int acpi_enabled;
void piix4_pm_init(PCIBus *bus, int devfn);
void acpi_bios_init(void);
+/* tpm_tis.c */
+int has_tpm_device(void);
+void tpm_tis_init(SetIRQFunc *set_irq, void *irq_opaque, int irq);
+
/* piix4acpi.c */
extern void pci_piix4_acpi_init(PCIBus *bus, int devfn);
@@ -1213,6 +1217,25 @@ void xenstore_check_new_media_present(int timeout);
void xenstore_write_vncport(int vnc_display);
int xenstore_read_vncpasswd(int domid);
+int xenstore_domain_has_devtype(struct xs_handle *handle,
+ const char *devtype);
+char **xenstore_domain_get_devices(struct xs_handle *handle,
+ const char *devtype, unsigned int *num);
+char *xenstore_read_hotplug_status(struct xs_handle *handle,
+ const char *devtype, const char *inst);
+char *xenstore_backend_read_variable(struct xs_handle *,
+ const char *devtype, const char *inst,
+ const char *var);
+int xenstore_subscribe_to_hotplug_status(struct xs_handle *handle,
+ const char *devtype,
+ const char *inst,
+ const char *token);
+int xenstore_unsubscribe_from_hotplug_status(struct xs_handle *handle,
+ const char *devtype,
+ const char *inst,
+ const char *token);
+
+
/* xen_platform.c */
void pci_xen_platform_init(PCIBus *bus);
diff --git a/tools/ioemu/vnc_keysym.h b/tools/ioemu/vnc_keysym.h
index a4ac6885bf..14fe47f9e1 100644
--- a/tools/ioemu/vnc_keysym.h
+++ b/tools/ioemu/vnc_keysym.h
@@ -271,5 +271,15 @@ static name2keysym_t name2keysym[]={
{"Num_Lock", 0xff7f}, /* XK_Num_Lock */
{"Pause", 0xff13}, /* XK_Pause */
{"Escape", 0xff1b}, /* XK_Escape */
+
+ /* localized keys */
+{"BackApostrophe", 0xff21},
+{"Muhenkan", 0xff22},
+{"Katakana", 0xff25},
+{"Zenkaku_Hankaku", 0xff29},
+{"Henkan_Mode_Real", 0xff23},
+{"Henkan_Mode_Ultra", 0xff3e},
+{"backslash_ja", 0xffa5},
+
{0,0},
};
diff --git a/tools/ioemu/xenstore.c b/tools/ioemu/xenstore.c
index ed978366b8..6c2bc9182a 100644
--- a/tools/ioemu/xenstore.c
+++ b/tools/ioemu/xenstore.c
@@ -264,3 +264,140 @@ int xenstore_read_vncpasswd(int domid)
return rc;
}
+
+
+/*
+ * get all device instances of a certain type
+ */
+char **xenstore_domain_get_devices(struct xs_handle *handle,
+ const char *devtype, unsigned int *num)
+{
+ char *path;
+ char *buf = NULL;
+ char **e = NULL;
+
+ path = xs_get_domain_path(handle, domid);
+ if (path == NULL)
+ goto out;
+
+ if (pasprintf(&buf, "%s/device/%s", path,devtype) == -1)
+ goto out;
+
+ e = xs_directory(handle, XBT_NULL, buf, num);
+
+ out:
+ free(path);
+ free(buf);
+ return e;
+}
+
+/*
+ * Check whether a domain has devices of the given type
+ */
+int xenstore_domain_has_devtype(struct xs_handle *handle, const char *devtype)
+{
+ int rc = 0;
+ unsigned int num;
+ char **e = xenstore_domain_get_devices(handle, devtype, &num);
+ if (e)
+ rc = 1;
+ free(e);
+ return rc;
+}
+
+/*
+ * Function that creates a path to a variable of an instance of a
+ * certain device
+ */
+static char *get_device_variable_path(const char *devtype, const char *inst,
+ const char *var)
+{
+ char *buf = NULL;
+ if (pasprintf(&buf, "/local/domain/0/backend/%s/%d/%s/%s",
+ devtype,
+ domid,
+ inst,
+ var) == -1) {
+ free(buf);
+ buf = NULL;
+ }
+ return buf;
+}
+
+char *xenstore_backend_read_variable(struct xs_handle *handle,
+ const char *devtype, const char *inst,
+ const char *var)
+{
+ char *value = NULL;
+ char *buf = NULL;
+ unsigned int len;
+
+ buf = get_device_variable_path(devtype, inst, var);
+ if (NULL == buf)
+ goto out;
+
+ value = xs_read(handle, XBT_NULL, buf, &len);
+
+ free(buf);
+
+out:
+ return value;
+}
+
+/*
+ Read the hotplug status variable from the backend given the type
+ of device and its instance.
+*/
+char *xenstore_read_hotplug_status(struct xs_handle *handle,
+ const char *devtype, const char *inst)
+{
+ return xenstore_backend_read_variable(handle, devtype, inst,
+ "hotplug-status");
+}
+
+/*
+ Subscribe to the hotplug status of a device given the type of device and
+ its instance.
+ In case an error occurrs, a negative number is returned.
+ */
+int xenstore_subscribe_to_hotplug_status(struct xs_handle *handle,
+ const char *devtype,
+ const char *inst,
+ const char *token)
+{
+ int rc = 0;
+ char *path = get_device_variable_path(devtype, inst, "hotplug-status");
+
+ if (path == NULL)
+ return -1;
+
+ if (0 == xs_watch(handle, path, token))
+ rc = -2;
+
+ free(path);
+
+ return rc;
+}
+
+/*
+ * Unsubscribe from a subscription to the status of a hotplug variable of
+ * a device.
+ */
+int xenstore_unsubscribe_from_hotplug_status(struct xs_handle *handle,
+ const char *devtype,
+ const char *inst,
+ const char *token)
+{
+ int rc = 0;
+ char *path;
+ path = get_device_variable_path(devtype, inst, "hotplug-status");
+ if (path == NULL)
+ return -1;
+
+ if (0 == xs_unwatch(handle, path, token))
+ rc = -2;
+
+ free(path);
+
+ return rc;
+}
diff --git a/tools/libfsimage/Makefile b/tools/libfsimage/Makefile
new file mode 100644
index 0000000000..394c7f4dfa
--- /dev/null
+++ b/tools/libfsimage/Makefile
@@ -0,0 +1,13 @@
+XEN_ROOT = ../..
+include $(XEN_ROOT)/tools/Rules.mk
+
+SUBDIRS-y = common ufs reiserfs
+SUBDIRS-y += $(shell ./check-libext2fs)
+
+.PHONY: all
+all install clean:
+ @set -e; for subdir in $(SUBDIRS-y); do \
+ $(MAKE) -C $$subdir $@; \
+ done
+
+distclean: clean
diff --git a/tools/libfsimage/Rules.mk b/tools/libfsimage/Rules.mk
new file mode 100644
index 0000000000..9fd15de4ce
--- /dev/null
+++ b/tools/libfsimage/Rules.mk
@@ -0,0 +1,32 @@
+include $(XEN_ROOT)/tools/Rules.mk
+
+DEPS = .*.d
+
+CFLAGS += -I$(XEN_ROOT)/tools/libfsimage/common/ -Werror -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -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 = lib/fs/$(FS)/64
+FSDIR-$(CONFIG_SunOS)-x86_32 = lib/fs/$(FS)/
+FSDIR-$(CONFIG_SunOS) = $(FSDIR-$(CONFIG_SunOS)-$(XEN_TARGET_ARCH))
+FSDIR = $(FSDIR-y)
+
+FSLIB = fsimage.so
+
+.PHONY: fs-all
+fs-all: $(FSLIB)
+
+.PHONY: fs-install
+fs-install: fs-all
+ $(INSTALL_DIR) $(DESTDIR)/usr/$(FSDIR)
+ $(INSTALL_PROG) $(FSLIB) $(DESTDIR)/usr/$(FSDIR)
+
+$(FSLIB): $(PIC_OBJS)
+ $(CC) $(CFLAGS) $(LDFLAGS) $(SHLIB_CFLAGS) -o $@ $^ -lfsimage $(FS_LIBDEPS)
+
+clean distclean:
+ rm -f $(PIC_OBJS) $(FSLIB)
+
+-include $(DEPS)
diff --git a/tools/libfsimage/check-libext2fs b/tools/libfsimage/check-libext2fs
new file mode 100755
index 0000000000..2f654cc4af
--- /dev/null
+++ b/tools/libfsimage/check-libext2fs
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+cat >ext2-test.c <<EOF
+#include <ext2fs/ext2fs.h>
+
+int main()
+{
+ ext2fs_open2;
+}
+EOF
+
+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 0000000000..98c43d5858
--- /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 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -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
+ [ -d $(DESTDIR)/usr/$(LIBDIR) ] || $(INSTALL_DIR) $(DESTDIR)/usr/$(LIBDIR)
+ [ -d $(DESTDIR)/usr/include ] || $(INSTALL_DIR) $(DESTDIR)/usr/include
+ $(INSTALL_PROG) libfsimage.so.$(MAJOR).$(MINOR) $(DESTDIR)/usr/$(LIBDIR)
+ ln -sf libfsimage.so.$(MAJOR).$(MINOR) $(DESTDIR)/usr/$(LIBDIR)/libfsimage.so.$(MAJOR)
+ ln -sf libfsimage.so.$(MAJOR) $(DESTDIR)/usr/$(LIBDIR)/libfsimage.so
+ $(INSTALL_DATA) fsimage.h $(DESTDIR)/usr/include
+ $(INSTALL_DATA) fsimage_plugin.h $(DESTDIR)/usr/include
+ $(INSTALL_DATA) fsimage_grub.h $(DESTDIR)/usr/include
+
+clean distclean:
+ rm -f $(PIC_OBJS) $(LIB)
+
+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 0000000000..a326fd7644
--- /dev/null
+++ b/tools/libfsimage/common/fsimage.c
@@ -0,0 +1,142 @@
+/*
+ * 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 <sys/stat.h>
+#include <sys/types.h>
+#include <strings.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pthread.h>
+
+#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)
+{
+ 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;
+
+ pthread_mutex_lock(&fsi_lock);
+ err = find_plugin(fsi, path);
+ 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);
+ fsip_fs_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);
+}
diff --git a/tools/libfsimage/common/fsimage.h b/tools/libfsimage/common/fsimage.h
new file mode 100644
index 0000000000..28165ef29d
--- /dev/null
+++ b/tools/libfsimage/common/fsimage.h
@@ -0,0 +1,52 @@
+/*
+ * 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_H
+#define _FSIMAGE_H
+
+#ifdef __cplusplus
+extern C {
+#endif
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <unistd.h>
+
+typedef struct fsi fsi_t;
+typedef struct fsi_file fsi_file_t;
+
+fsi_t *fsi_open_fsimage(const char *, uint64_t);
+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);
+
+#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 0000000000..f8c3839328
--- /dev/null
+++ b/tools/libfsimage/common/fsimage_grub.c
@@ -0,0 +1,276 @@
+/*
+ * 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 __sun__
+#define _XOPEN_SOURCE 500
+#endif
+#include <stdlib.h>
+#include <strings.h>
+#include <errno.h>
+
+#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);
+}
+
+int
+fsig_devread(fsi_file_t *ffi, unsigned int sector, unsigned int offset,
+ unsigned int bufsize, char *buf)
+{
+ uint64_t off = ffi->ff_fsi->f_off + ((uint64_t)(sector * 512)) + offset;
+ ssize_t bytes_read = 0;
+
+ while (bufsize) {
+ ssize_t ret = pread(ffi->ff_fsi->f_fd, buf + bytes_read,
+ bufsize, (off_t)off);
+ if (ret == -1)
+ return (0);
+ if (ret == 0)
+ return (0);
+
+ bytes_read += ret;
+ bufsize -= ret;
+ }
+
+ 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)
+{
+ 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)) {
+ fsip_file_free(ffi);
+ 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)
+{
+ 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)
+{
+ 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 0000000000..38db2a219c
--- /dev/null
+++ b/tools/libfsimage/common/fsimage_grub.h
@@ -0,0 +1,92 @@
+/*
+ * 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_GRUB_H
+#define _FSIMAGE_GRUB_H
+
+#ifdef __cplusplus
+extern C {
+#endif
+
+#include <sys/types.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "fsimage.h"
+#include "fsimage_plugin.h"
+
+typedef struct fsig_plugin_ops {
+ int fpo_version;
+ int (*fpo_mount)(fsi_file_t *);
+ 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 0x8000
+#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 grub_memset memset
+#define grub_memmove memmove
+
+extern char **fsig_disk_read_junk(void);
+
+#define ERR_FSYS_CORRUPT 1
+#define ERR_SYMLINK_LOOP 1
+#define ERR_FILELENGTH 1
+#define ERR_BAD_FILETYPE 1
+#define ERR_BAD_FILETYPE 1
+#define ERR_FILE_NOT_FOUND 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 0000000000..71099ba2ff
--- /dev/null
+++ b/tools/libfsimage/common/fsimage_plugin.c
@@ -0,0 +1,214 @@
+/*
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <strings.h>
+#include <string.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <errno.h>
+
+#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;
+}
+
+void
+fsip_fs_free(fsi_t *fsi)
+{
+ free(fsi->f_data);
+ free(fsi);
+}
+
+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->ff_data);
+ 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;
+
+#ifdef __sun__
+ if (fsdir == NULL)
+ fsdir = "/usr/lib/fs";
+
+ if (sizeof(void *) == 8)
+ isadir = "64/";
+#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)
+{
+ 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) == 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 0000000000..4592e08be7
--- /dev/null
+++ b/tools/libfsimage/common/fsimage_plugin.h
@@ -0,0 +1,65 @@
+/*
+ * 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 <sys/types.h>
+
+#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 *);
+ 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 *);
+void fsip_fs_free(fsi_t *);
+fsi_file_t *fsip_file_alloc(fsi_t *, void *);
+void fsip_file_free(fsi_file_t *);
+fsi_t * fsip_fs(fsi_file_t *ffi);
+uint64_t fsip_fs_offset(fsi_t *fsi);
+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 0000000000..cf0dfe6612
--- /dev/null
+++ b/tools/libfsimage/common/fsimage_priv.h
@@ -0,0 +1,62 @@
+/*
+ * 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_PRIV_H
+#define _FSIMAGE_PRIV_H
+
+#ifdef __cplusplus
+extern C {
+#endif
+
+#include <sys/types.h>
+
+#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;
+};
+
+struct fsi_file {
+ fsi_t *ff_fsi;
+ void *ff_data;
+};
+
+int find_plugin(fsi_t *, 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 0000000000..f15060cba4
--- /dev/null
+++ b/tools/libfsimage/common/mapfile-GNU
@@ -0,0 +1,37 @@
+VERSION {
+ libfsimage.so.1.1 {
+ global:
+ fsi_open_fsimage;
+ fsi_close_fsimage;
+ fsi_file_exists;
+ fsi_open_file;
+ fsi_close_file;
+ fsi_read_file;
+ fsi_pread_file;
+
+ fsip_fs_set_data;
+ fsip_fs_free;
+ fsip_file_alloc;
+ fsip_file_free;
+ fsip_fs;
+ fsip_fs_offset;
+ fsip_fs_data;
+ fsip_file_data;
+
+ fsig_init;
+ fsig_devread;
+ fsig_substring;
+ 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 0000000000..1ea38bed04
--- /dev/null
+++ b/tools/libfsimage/common/mapfile-SunOS
@@ -0,0 +1,35 @@
+libfsimage.so.1.1 {
+ global:
+ fsi_open_fsimage;
+ fsi_close_fsimage;
+ fsi_file_exists;
+ fsi_open_file;
+ fsi_close_file;
+ fsi_read_file;
+ fsi_pread_file;
+
+ fsip_fs_set_data;
+ fsip_fs_free;
+ fsip_file_alloc;
+ fsip_file_free;
+ fsip_fs;
+ fsip_fs_data;
+ fsip_fs_offset;
+ fsip_file_data;
+
+ fsig_init;
+ fsig_devread;
+ fsig_substring;
+ 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 0000000000..a60b3a58a5
--- /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 0000000000..b6f60ce65f
--- /dev/null
+++ b/tools/libfsimage/ext2fs-lib/ext2fs-lib.c
@@ -0,0 +1,172 @@
+/*
+ * 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 <fsimage_plugin.h>
+#include <ext2fs/ext2fs.h>
+#include <errno.h>
+#include <inttypes.h>
+
+static int
+ext2lib_mount(fsi_t *fsi, const char *name)
+{
+ 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) {
+ errno = EINVAL;
+ return (-1);
+ }
+ 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 0000000000..43a4501f5d
--- /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 0000000000..d6c305238e
--- /dev/null
+++ b/tools/libfsimage/ext2fs/fsys_ext2fs.c
@@ -0,0 +1,804 @@
+/*
+ * 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 <fsimage_grub.h>
+
+#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 */
+ __u32 s_reserved[235]; /* 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) ffz(~(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)))
+
+/* 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)
+
+/* include/asm-i386/bitops.h */
+/*
+ * ffz = Find First Zero in word. Undefined if no zero exists,
+ * so code should check against ~0UL first..
+ */
+#ifdef __amd64
+#define BSF "bsfq"
+#else
+#define BSF "bsfl"
+#endif
+static __inline__ unsigned long
+ffz (unsigned long word)
+{
+ __asm__ (BSF " %1,%0"
+: "=r" (word)
+: "r" (~word));
+ return word;
+}
+
+/* check filesystem types and read superblock into memory buffer */
+int
+ext2fs_mount (fsi_file_t *ffi)
+{
+ 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 */
+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
+ */
+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_BLOCK_SIZE (SUPERBLOCK) / sizeof (struct ext2_inode)));
+#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 = INODE +
+ ((current_ino - 1)
+ & (EXT2_BLOCK_SIZE (SUPERBLOCK) / sizeof (struct ext2_inode) - 1));
+#ifdef E2DEBUG
+ printf ("ipb=%d, sizeof(inode)=%d\n",
+ (EXT2_BLOCK_SIZE (SUPERBLOCK) / sizeof (struct ext2_inode)),
+ sizeof (struct ext2_inode));
+ 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 (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 (*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 (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/reiserfs/Makefile b/tools/libfsimage/reiserfs/Makefile
new file mode 100644
index 0000000000..c71fff8843
--- /dev/null
+++ b/tools/libfsimage/reiserfs/Makefile
@@ -0,0 +1,13 @@
+XEN_ROOT = ../../..
+
+LIB_SRCS-y = fsys_reiserfs.c
+
+FS = reiserfs
+
+.PHONY: all
+all: fs-all
+
+.PHONY: install
+install: fs-install
+
+include $(XEN_ROOT)/tools/libfsimage/Rules.mk
diff --git a/tools/libfsimage/reiserfs/fsys_reiserfs.c b/tools/libfsimage/reiserfs/fsys_reiserfs.c
new file mode 100644
index 0000000000..132821d7ac
--- /dev/null
+++ b/tools/libfsimage/reiserfs/fsys_reiserfs.c
@@ -0,0 +1,1254 @@
+/* fsys_reiserfs.c - an implementation for the ReiserFS filesystem */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <fsimage_grub.h>
+
+#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))
+
+#ifdef __amd64
+#define BSF "bsfq"
+#else
+#define BSF "bsfl"
+#endif
+static __inline__ unsigned long
+grub_log2 (unsigned long word)
+{
+ __asm__ (BSF " %1,%0"
+ : "=r" (word)
+ : "r" (word));
+ return word;
+}
+#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 */
+int
+reiserfs_mount (fsi_file_t *ffi)
+{
+ 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;
+}
+
+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.
+ */
+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 (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 (*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 (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 0000000000..b7218c2b3f
--- /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 0000000000..f1cb917c8c
--- /dev/null
+++ b/tools/libfsimage/ufs/fsys_ufs.c
@@ -0,0 +1,276 @@
+/*
+ * 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 <fsimage_grub.h>
+
+#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 INDIRBLK1 ((grub_daddr32_t *)(FSYS_BUF + 0x4000)) /* 2+ indir blk */
+#define INDIRBLK0 ((grub_daddr32_t *)(FSYS_BUF+ 0x6000)) /* 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 */
+int
+ufs_mount(fsi_file_t *ffi)
+{
+ if (/*! IS_PC_SLICE_TYPE_SOLARIS(current_slice) || */
+ !devread(ffi, UFS_SBLOCK, 0, UFS_SBSIZE, (char *)SUPERBLOCK) ||
+ SUPERBLOCK->fs_magic != UFS_MAGIC)
+ 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.
+ */
+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(*dirname)) {
+ if (!openi(ffi, inode))
+ return 0;
+
+ /* parse for next path component */
+ fname = dirname;
+ while (*dirname && !isspace(*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.
+ */
+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 0000000000..4e7c736c6d
--- /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/libxc/ia64/xc_ia64_hvm_build.c b/tools/libxc/ia64/xc_ia64_hvm_build.c
index 0caaf343b3..ad5c6fdabd 100644
--- a/tools/libxc/ia64/xc_ia64_hvm_build.c
+++ b/tools/libxc/ia64/xc_ia64_hvm_build.c
@@ -618,7 +618,7 @@ error_out:
int
xc_hvm_build(int xc_handle, uint32_t domid, int memsize,
const char *image_name, unsigned int vcpus, unsigned int pae,
- unsigned int acpi, unsigned int apic, unsigned int store_evtchn,
+ unsigned int acpi, unsigned int store_evtchn,
unsigned long *store_mfn)
{
struct xen_domctl launch_domctl, domctl;
diff --git a/tools/libxc/xc_core.c b/tools/libxc/xc_core.c
index efaf397abf..550d5691e1 100644
--- a/tools/libxc/xc_core.c
+++ b/tools/libxc/xc_core.c
@@ -62,7 +62,7 @@ xc_domain_dumpcore_via_callback(int xc_handle,
nr_pages = info.nr_pages;
- header.xch_magic = XC_CORE_MAGIC;
+ 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_ctxt_offset = sizeof(struct xc_core_header);
diff --git a/tools/libxc/xc_domain.c b/tools/libxc/xc_domain.c
index d559bdb742..7936d00dc9 100644
--- a/tools/libxc/xc_domain.c
+++ b/tools/libxc/xc_domain.c
@@ -12,6 +12,7 @@
int xc_domain_create(int xc_handle,
uint32_t ssidref,
xen_domain_handle_t handle,
+ uint32_t flags,
uint32_t *pdomid)
{
int err;
@@ -20,6 +21,7 @@ int xc_domain_create(int xc_handle,
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;
@@ -169,15 +171,16 @@ int xc_domain_getinfo(int xc_handle,
break;
info->domid = (uint16_t)domctl.domain;
- info->dying = !!(domctl.u.getdomaininfo.flags & DOMFLAGS_DYING);
- info->shutdown = !!(domctl.u.getdomaininfo.flags & DOMFLAGS_SHUTDOWN);
- info->paused = !!(domctl.u.getdomaininfo.flags & DOMFLAGS_PAUSED);
- info->blocked = !!(domctl.u.getdomaininfo.flags & DOMFLAGS_BLOCKED);
- info->running = !!(domctl.u.getdomaininfo.flags & DOMFLAGS_RUNNING);
+ 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->shutdown_reason =
- (domctl.u.getdomaininfo.flags>>DOMFLAGS_SHUTDOWNSHIFT) &
- DOMFLAGS_SHUTDOWNMASK;
+ (domctl.u.getdomaininfo.flags>>XEN_DOMINF_shutdownshift) &
+ XEN_DOMINF_shutdownmask;
if ( info->shutdown && (info->shutdown_reason == SHUTDOWN_crash) )
{
@@ -200,7 +203,8 @@ int xc_domain_getinfo(int xc_handle,
info++;
}
- if( !nr_doms ) return rc;
+ if ( nr_doms == 0 )
+ return rc;
return nr_doms;
}
@@ -345,7 +349,7 @@ int xc_domain_memory_increase_reservation(int xc_handle,
if ( err == nr_extents )
return 0;
- if ( err > 0 )
+ if ( err >= 0 )
{
DPRINTF("Failed allocation for dom %d: "
"%ld pages order %d addr_bits %d\n",
@@ -384,11 +388,11 @@ int xc_domain_memory_decrease_reservation(int xc_handle,
if ( err == nr_extents )
return 0;
- if ( err > 0 )
+ if ( err >= 0 )
{
DPRINTF("Failed deallocation for dom %d: %ld pages order %d\n",
domid, nr_extents, extent_order);
- errno = EBUSY;
+ errno = EINVAL;
err = -1;
}
@@ -415,7 +419,7 @@ int xc_domain_memory_populate_physmap(int xc_handle,
if ( err == nr_extents )
return 0;
- if ( err > 0 )
+ if ( err >= 0 )
{
DPRINTF("Failed allocation for dom %d: %ld pages order %d\n",
domid, nr_extents, extent_order);
diff --git a/tools/libxc/xc_hvm_build.c b/tools/libxc/xc_hvm_build.c
index 5d81491bc5..fe301207f1 100644
--- a/tools/libxc/xc_hvm_build.c
+++ b/tools/libxc/xc_hvm_build.c
@@ -12,7 +12,6 @@
#include <unistd.h>
#include <zlib.h>
#include <xen/hvm/hvm_info_table.h>
-#include <xen/hvm/ioreq.h>
#include <xen/hvm/params.h>
#include <xen/hvm/e820.h>
@@ -57,88 +56,67 @@ static void build_e820map(void *e820_page, unsigned long long mem_size)
unsigned char nr_map = 0;
/*
- * physical address space from HVM_BELOW_4G_RAM_END to 4G is reserved
+ * 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 ) {
+ 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-0x9F000: Ordinary RAM. */
e820entry[nr_map].addr = 0x0;
e820entry[nr_map].size = 0x9F000;
e820entry[nr_map].type = E820_RAM;
nr_map++;
+ /*
+ * 0x9F000-0x9F800: SMBIOS tables.
+ * 0x9FC00-0xA0000: Extended BIOS Data Area (EBDA).
+ * TODO: SMBIOS tables should be moved higher (>=0xE0000).
+ * They are unusually low in our memory map: could cause problems?
+ */
e820entry[nr_map].addr = 0x9F000;
e820entry[nr_map].size = 0x1000;
e820entry[nr_map].type = E820_RESERVED;
nr_map++;
- e820entry[nr_map].addr = 0xA0000;
- e820entry[nr_map].size = 0x20000;
- e820entry[nr_map].type = E820_IO;
- nr_map++;
-
- e820entry[nr_map].addr = 0xEA000;
- e820entry[nr_map].size = 0x01000;
- e820entry[nr_map].type = E820_ACPI;
- 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.
+ */
- e820entry[nr_map].addr = 0xF0000;
- e820entry[nr_map].size = 0x10000;
+ /*
+ * 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++;
-/* buffered io page. */
-#define BUFFERED_IO_PAGES 1
-/* xenstore page. */
-#define XENSTORE_PAGES 1
-/* shared io page. */
-#define SHARED_IO_PAGES 1
-/* totally 16 static pages are reserved in E820 table */
-
- /* Most of the ram goes here */
+ /* Low RAM goes here. Remove 3 pages for ioreq, bufioreq, and xenstore. */
e820entry[nr_map].addr = 0x100000;
- e820entry[nr_map].size = mem_size - 0x100000 - PAGE_SIZE *
- (BUFFERED_IO_PAGES +
- XENSTORE_PAGES +
- SHARED_IO_PAGES);
+ e820entry[nr_map].size = mem_size - 0x100000 - PAGE_SIZE * 3;
e820entry[nr_map].type = E820_RAM;
nr_map++;
- /* Statically allocated special pages */
-
- /* For buffered IO requests */
- e820entry[nr_map].addr = mem_size - PAGE_SIZE *
- (BUFFERED_IO_PAGES +
- XENSTORE_PAGES +
- SHARED_IO_PAGES);
- e820entry[nr_map].size = PAGE_SIZE * BUFFERED_IO_PAGES;
- e820entry[nr_map].type = E820_BUFFERED_IO;
- nr_map++;
-
- /* For xenstore */
- e820entry[nr_map].addr = mem_size - PAGE_SIZE *
- (XENSTORE_PAGES +
- SHARED_IO_PAGES);
- e820entry[nr_map].size = PAGE_SIZE * XENSTORE_PAGES;
- e820entry[nr_map].type = E820_XENSTORE;
- nr_map++;
-
- /* Shared ioreq_t page */
- e820entry[nr_map].addr = mem_size - PAGE_SIZE * SHARED_IO_PAGES;
- e820entry[nr_map].size = PAGE_SIZE * SHARED_IO_PAGES;
- e820entry[nr_map].type = E820_SHARED_PAGE;
- nr_map++;
-
- e820entry[nr_map].addr = 0xFEC00000;
- e820entry[nr_map].size = 0x1400000;
- e820entry[nr_map].type = E820_IO;
- nr_map++;
-
- if ( extra_mem_size ) {
+ if ( extra_mem_size )
+ {
e820entry[nr_map].addr = (1ULL << 32);
e820entry[nr_map].size = extra_mem_size;
e820entry[nr_map].type = E820_RAM;
@@ -197,28 +175,22 @@ static int set_hvm_info(int xc_handle, uint32_t dom,
static int setup_guest(int xc_handle,
uint32_t dom, int memsize,
char *image, unsigned long image_size,
- unsigned long nr_pages,
vcpu_guest_context_t *ctxt,
unsigned long shared_info_frame,
unsigned int vcpus,
unsigned int pae,
unsigned int acpi,
- unsigned int apic,
unsigned int store_evtchn,
unsigned long *store_mfn)
{
xen_pfn_t *page_array = NULL;
- unsigned long count, i;
- unsigned long long ptr;
- xc_mmu_t *mmu = NULL;
-
+ unsigned long i, nr_pages = (unsigned long)memsize << (20 - PAGE_SHIFT);
+ unsigned long shared_page_nr;
shared_info_t *shared_info;
void *e820_page;
-
struct domain_setup_info dsi;
uint64_t v_end;
-
- unsigned long shared_page_nr;
+ int rc;
memset(&dsi, 0, sizeof(struct domain_setup_info));
@@ -231,7 +203,6 @@ static int setup_guest(int xc_handle,
goto error_out;
}
- /* memsize is in megabytes */
v_end = (unsigned long long)memsize << 20;
IPRINTF("VIRTUAL MEMORY ARRANGEMENT:\n"
@@ -256,40 +227,33 @@ static int setup_guest(int xc_handle,
goto error_out;
}
- if ( xc_get_pfn_list(xc_handle, dom, page_array, nr_pages) != nr_pages )
+ 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. */
+ rc = xc_domain_memory_populate_physmap(
+ xc_handle, dom, (nr_pages > 0xa0) ? 0xa0 : nr_pages,
+ 0, 0, &page_array[0x00]);
+ if ( (rc == 0) && (nr_pages > 0xc0) )
+ rc = xc_domain_memory_populate_physmap(
+ xc_handle, dom, nr_pages - 0xc0, 0, 0, &page_array[0xc0]);
+ if ( rc != 0 )
{
- PERROR("Could not get the page frame list.\n");
+ PERROR("Could not allocate memory for HVM guest.\n");
goto error_out;
}
- loadelfimage(image, xc_handle, dom, page_array, &dsi);
-
- if ( (mmu = xc_init_mmu_updates(xc_handle, dom)) == NULL )
- goto error_out;
-
- /* Write the machine->phys table entries. */
- for ( count = 0; count < nr_pages; count++ )
+ if ( xc_domain_translate_gpfn_list(xc_handle, dom, nr_pages,
+ page_array, page_array) )
{
- unsigned long gpfn_count_skip;
-
- ptr = (unsigned long long)page_array[count] << PAGE_SHIFT;
-
- gpfn_count_skip = 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 ( count >= (HVM_BELOW_4G_RAM_END >> PAGE_SHIFT) )
- gpfn_count_skip = HVM_BELOW_4G_MMIO_LENGTH >> PAGE_SHIFT;
-
- if ( xc_add_mmu_update(xc_handle, mmu,
- ptr | MMU_MACHPHYS_UPDATE,
- count + gpfn_count_skip) )
- goto error_out;
+ PERROR("Could not translate addresses of HVM guest.\n");
+ goto error_out;
}
+ loadelfimage(image, xc_handle, dom, page_array, &dsi);
+
if ( set_hvm_info(xc_handle, dom, page_array, vcpus, acpi) )
{
ERROR("Couldn't set hvm info for HVM guest.\n");
@@ -297,7 +261,6 @@ static int setup_guest(int xc_handle,
}
xc_set_hvm_param(xc_handle, dom, HVM_PARAM_PAE_ENABLED, pae);
- xc_set_hvm_param(xc_handle, dom, HVM_PARAM_APIC_ENABLED, apic);
if ( (e820_page = xc_map_foreign_range(
xc_handle, dom, PAGE_SIZE, PROT_READ | PROT_WRITE,
@@ -316,6 +279,8 @@ static int setup_guest(int xc_handle,
/* Mask all upcalls... */
for ( i = 0; i < MAX_VIRT_CPUS; i++ )
shared_info->vcpu_info[i].evtchn_upcall_mask = 1;
+ memset(&shared_info->evtchn_mask[0], 0xff,
+ sizeof(shared_info->evtchn_mask));
munmap(shared_info, PAGE_SIZE);
if ( v_end > HVM_BELOW_4G_RAM_END )
@@ -323,39 +288,25 @@ static int setup_guest(int xc_handle,
else
shared_page_nr = (v_end >> PAGE_SHIFT) - 1;
- *store_mfn = page_array[shared_page_nr - 1];
-
- xc_set_hvm_param(xc_handle, dom, HVM_PARAM_STORE_PFN, shared_page_nr - 1);
- xc_set_hvm_param(xc_handle, dom, HVM_PARAM_STORE_EVTCHN, store_evtchn);
-
- /* Paranoia */
- /* clean the shared IO requests page */
- if ( xc_clear_domain_page(xc_handle, dom, page_array[shared_page_nr]) )
- goto error_out;
-
- /* clean the buffered IO requests page */
- if ( xc_clear_domain_page(xc_handle, dom, page_array[shared_page_nr - 2]) )
- goto error_out;
-
- if ( xc_clear_domain_page(xc_handle, dom, *store_mfn) )
+ /* Paranoia: clean pages. */
+ if ( xc_clear_domain_page(xc_handle, dom, page_array[shared_page_nr]) ||
+ xc_clear_domain_page(xc_handle, dom, page_array[shared_page_nr-1]) ||
+ xc_clear_domain_page(xc_handle, dom, page_array[shared_page_nr-2]) )
goto error_out;
- /* Send the page update requests down to the hypervisor. */
- if ( xc_finish_mmu_updates(xc_handle, mmu) )
- goto error_out;
+ *store_mfn = page_array[shared_page_nr - 1];
+ xc_set_hvm_param(xc_handle, dom, HVM_PARAM_STORE_PFN, shared_page_nr-1);
+ xc_set_hvm_param(xc_handle, dom, HVM_PARAM_STORE_EVTCHN, store_evtchn);
+ xc_set_hvm_param(xc_handle, dom, HVM_PARAM_BUFIOREQ_PFN, shared_page_nr-2);
+ xc_set_hvm_param(xc_handle, dom, HVM_PARAM_IOREQ_PFN, shared_page_nr);
- free(mmu);
free(page_array);
- /*
- * Initial register values:
- */
ctxt->user_regs.eip = dsi.v_kernentry;
return 0;
error_out:
- free(mmu);
free(page_array);
return -1;
}
@@ -368,15 +319,12 @@ static int xc_hvm_build_internal(int xc_handle,
unsigned int vcpus,
unsigned int pae,
unsigned int acpi,
- unsigned int apic,
unsigned int store_evtchn,
unsigned long *store_mfn)
{
struct xen_domctl launch_domctl, domctl;
- int rc, i;
- vcpu_guest_context_t st_ctxt, *ctxt = &st_ctxt;
- unsigned long nr_pages;
- xen_capabilities_info_t xen_caps;
+ vcpu_guest_context_t ctxt;
+ int rc;
if ( (image == NULL) || (image_size == 0) )
{
@@ -384,31 +332,6 @@ static int xc_hvm_build_internal(int xc_handle,
goto error_out;
}
- if ( (rc = xc_version(xc_handle, XENVER_capabilities, &xen_caps)) != 0 )
- {
- PERROR("Failed to get xen version info");
- goto error_out;
- }
-
- if ( !strstr(xen_caps, "hvm") )
- {
- PERROR("CPU doesn't support HVM extensions or "
- "the extensions are not enabled");
- goto error_out;
- }
-
- if ( (nr_pages = xc_get_tot_pages(xc_handle, domid)) < 0 )
- {
- PERROR("Could not find total pages for domain");
- goto error_out;
- }
-
- if ( lock_pages(&st_ctxt, sizeof(st_ctxt) ) )
- {
- PERROR("%s: ctxt mlock failed", __func__);
- return 1;
- }
-
domctl.cmd = XEN_DOMCTL_getdomaininfo;
domctl.domain = (domid_t)domid;
if ( (xc_domctl(xc_handle, &domctl) < 0) ||
@@ -418,69 +341,31 @@ static int xc_hvm_build_internal(int xc_handle,
goto error_out;
}
- /* HVM domains must be put into shadow mode at the start of day */
- if ( xc_shadow_control(xc_handle, domid, XEN_DOMCTL_SHADOW_OP_ENABLE,
- NULL, 0, NULL,
- XEN_DOMCTL_SHADOW_ENABLE_REFCOUNT |
- XEN_DOMCTL_SHADOW_ENABLE_TRANSLATE |
- XEN_DOMCTL_SHADOW_ENABLE_EXTERNAL,
- NULL) )
- {
- PERROR("Could not enable shadow paging for domain.\n");
- goto error_out;
- }
-
- memset(ctxt, 0, sizeof(*ctxt));
+ memset(&ctxt, 0, sizeof(ctxt));
- ctxt->flags = VGCF_HVM_GUEST;
- if ( setup_guest(xc_handle, domid, memsize, image, image_size, nr_pages,
- ctxt, domctl.u.getdomaininfo.shared_info_frame,
- vcpus, pae, acpi, apic, store_evtchn, store_mfn) < 0)
+ if ( setup_guest(xc_handle, domid, memsize, image, image_size,
+ &ctxt, domctl.u.getdomaininfo.shared_info_frame,
+ vcpus, pae, acpi, store_evtchn, store_mfn) < 0)
{
ERROR("Error constructing guest OS");
goto error_out;
}
- /* FPU is set up to default initial state. */
- memset(&ctxt->fpu_ctxt, 0, sizeof(ctxt->fpu_ctxt));
-
- /* Virtual IDT is empty at start-of-day. */
- for ( i = 0; i < 256; i++ )
+ if ( lock_pages(&ctxt, sizeof(ctxt) ) )
{
- ctxt->trap_ctxt[i].vector = i;
- ctxt->trap_ctxt[i].cs = FLAT_KERNEL_CS;
+ PERROR("%s: ctxt mlock failed", __func__);
+ goto error_out;
}
- /* No LDT. */
- ctxt->ldt_ents = 0;
-
- /* Use the default Xen-provided GDT. */
- ctxt->gdt_ents = 0;
-
- /* No debugging. */
- memset(ctxt->debugreg, 0, sizeof(ctxt->debugreg));
-
- /* No callback handlers. */
-#if defined(__i386__)
- ctxt->event_callback_cs = FLAT_KERNEL_CS;
- ctxt->event_callback_eip = 0;
- ctxt->failsafe_callback_cs = FLAT_KERNEL_CS;
- ctxt->failsafe_callback_eip = 0;
-#elif defined(__x86_64__)
- ctxt->event_callback_eip = 0;
- ctxt->failsafe_callback_eip = 0;
- ctxt->syscall_callback_eip = 0;
-#endif
-
memset(&launch_domctl, 0, sizeof(launch_domctl));
-
launch_domctl.domain = (domid_t)domid;
launch_domctl.u.vcpucontext.vcpu = 0;
- set_xen_guest_handle(launch_domctl.u.vcpucontext.ctxt, ctxt);
-
+ set_xen_guest_handle(launch_domctl.u.vcpucontext.ctxt, &ctxt);
launch_domctl.cmd = XEN_DOMCTL_setvcpucontext;
rc = xc_domctl(xc_handle, &launch_domctl);
+ unlock_pages(&ctxt, sizeof(ctxt));
+
return rc;
error_out:
@@ -626,7 +511,6 @@ int xc_hvm_build(int xc_handle,
unsigned int vcpus,
unsigned int pae,
unsigned int acpi,
- unsigned int apic,
unsigned int store_evtchn,
unsigned long *store_mfn)
{
@@ -640,7 +524,7 @@ int xc_hvm_build(int xc_handle,
sts = xc_hvm_build_internal(xc_handle, domid, memsize,
image, image_size,
- vcpus, pae, acpi, apic,
+ vcpus, pae, acpi,
store_evtchn, store_mfn);
free(image);
@@ -662,7 +546,6 @@ int xc_hvm_build_mem(int xc_handle,
unsigned int vcpus,
unsigned int pae,
unsigned int acpi,
- unsigned int apic,
unsigned int store_evtchn,
unsigned long *store_mfn)
{
@@ -687,7 +570,7 @@ int xc_hvm_build_mem(int xc_handle,
sts = xc_hvm_build_internal(xc_handle, domid, memsize,
img, img_len,
- vcpus, pae, acpi, apic,
+ vcpus, pae, acpi,
store_evtchn, store_mfn);
/* xc_inflate_buffer may return the original buffer pointer (for
diff --git a/tools/libxc/xc_linux_build.c b/tools/libxc/xc_linux_build.c
index a95ebd5c54..af9a63cced 100644
--- a/tools/libxc/xc_linux_build.c
+++ b/tools/libxc/xc_linux_build.c
@@ -25,17 +25,16 @@
#define L4_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_DIRTY|_PAGE_USER)
#endif
-#ifdef __ia64__
-#define get_tot_pages xc_get_max_pages
-#else
-#define get_tot_pages xc_get_tot_pages
-#endif
-
#define round_pgup(_p) (((_p)+(PAGE_SIZE-1))&PAGE_MASK)
#define round_pgdown(_p) ((_p)&PAGE_MASK)
struct initrd_info {
enum { INITRD_none, INITRD_file, INITRD_mem } type;
+ /*
+ * .len must be filled in by the user for type==INITRD_mem. It is
+ * filled in by load_initrd() for INITRD_file and unused for
+ * INITRD_none.
+ */
unsigned long len;
union {
gzFile file_handle;
@@ -134,30 +133,42 @@ static int load_initrd(int xc_handle, domid_t dom,
xen_pfn_t *phys_to_mach)
{
char page[PAGE_SIZE];
- unsigned long pfn_start, pfn, nr_pages;
+ unsigned long pfn_start, pfn;
if ( initrd->type == INITRD_none )
return 0;
pfn_start = physbase >> PAGE_SHIFT;
- nr_pages = (initrd->len + PAGE_SIZE - 1) >> PAGE_SHIFT;
- for ( pfn = pfn_start; pfn < (pfn_start + nr_pages); pfn++ )
+ if ( initrd->type == INITRD_mem )
{
- if ( initrd->type == INITRD_mem )
+ unsigned long nr_pages = (initrd->len + PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+ for ( pfn = pfn_start; pfn < (pfn_start + nr_pages); pfn++ )
{
xc_copy_to_domain_page(
xc_handle, dom, phys_to_mach[pfn],
&initrd->u.mem_addr[(pfn - pfn_start) << PAGE_SHIFT]);
}
- else
+ }
+ else
+ {
+ int readlen;
+
+ pfn = pfn_start;
+ initrd->len = 0;
+
+ /* gzread returns 0 on EOF */
+ while ( (readlen = gzread(initrd->u.file_handle, page, PAGE_SIZE)) )
{
- if ( gzread(initrd->u.file_handle, page, PAGE_SIZE) == -1 )
+ if ( readlen < 0 )
{
PERROR("Error reading initrd image, could not");
return -EINVAL;
}
- xc_copy_to_domain_page(xc_handle, dom, phys_to_mach[pfn], page);
+
+ initrd->len += readlen;
+ xc_copy_to_domain_page(xc_handle, dom, phys_to_mach[pfn++], page);
}
}
@@ -485,10 +496,17 @@ static int setup_guest(int xc_handle,
if ( rc != 0 )
goto error_out;
- dsi.v_start = round_pgdown(dsi.v_start);
- vinitrd_start = round_pgup(dsi.v_end);
- vinitrd_end = vinitrd_start + initrd->len;
- v_end = round_pgup(vinitrd_end);
+ dsi.v_start = round_pgdown(dsi.v_start);
+ (load_funcs.loadimage)(image, image_size, xc_handle, dom, page_array,
+ &dsi);
+
+ vinitrd_start = round_pgup(dsi.v_end);
+ if ( load_initrd(xc_handle, dom, initrd,
+ vinitrd_start - dsi.v_start, page_array) )
+ goto error_out;
+
+ vinitrd_end = vinitrd_start + initrd->len;
+ v_end = round_pgup(vinitrd_end);
start_info_mpa = (nr_pages - 3) << PAGE_SHIFT;
/* Build firmware. */
@@ -525,13 +543,6 @@ static int setup_guest(int xc_handle,
_p(dsi.v_start), _p(v_end));
IPRINTF(" ENTRY ADDRESS: %p\n", _p(dsi.v_kernentry));
- (load_funcs.loadimage)(image, image_size, xc_handle, dom, page_array,
- &dsi);
-
- if ( load_initrd(xc_handle, dom, initrd,
- vinitrd_start - dsi.v_start, page_array) )
- goto error_out;
-
*pvke = dsi.v_kernentry;
/* Now need to retrieve machine pfn for system pages:
@@ -657,7 +668,6 @@ static int setup_guest(int xc_handle,
int hypercall_page_defined;
start_info_t *start_info;
shared_info_t *shared_info;
- xc_mmu_t *mmu = NULL;
const char *p;
DECLARE_DOMCTL;
int rc;
@@ -699,7 +709,7 @@ static int setup_guest(int xc_handle,
goto error_out;
}
- if (!compat_check(xc_handle, &dsi))
+ if ( !compat_check(xc_handle, &dsi) )
goto error_out;
/* Parse and validate kernel features. */
@@ -728,6 +738,28 @@ static int setup_guest(int xc_handle,
shadow_mode_enabled = test_feature_bit(XENFEAT_auto_translated_physmap,
required_features);
+ if ( (page_array = malloc(nr_pages * sizeof(unsigned long))) == NULL )
+ {
+ PERROR("Could not allocate memory");
+ goto error_out;
+ }
+
+ for ( i = 0; i < nr_pages; i++ )
+ page_array[i] = i;
+
+ if ( xc_domain_memory_populate_physmap(xc_handle, dom, nr_pages,
+ 0, 0, page_array) )
+ {
+ PERROR("Could not allocate memory for PV guest.\n");
+ goto error_out;
+ }
+
+ rc = (load_funcs.loadimage)(image, image_size,
+ xc_handle, dom, page_array,
+ &dsi);
+ if ( rc != 0 )
+ goto error_out;
+
/*
* Why do we need this? The number of page-table frames depends on the
* size of the bootstrap address space. But the size of the address space
@@ -741,9 +773,14 @@ static int setup_guest(int xc_handle,
ERROR("End of mapped kernel image too close to end of memory");
goto error_out;
}
+
vinitrd_start = v_end;
+ if ( load_initrd(xc_handle, dom, initrd,
+ vinitrd_start - dsi.v_start, page_array) )
+ goto error_out;
if ( !increment_ulong(&v_end, round_pgup(initrd->len)) )
goto error_out;
+
vphysmap_start = v_end;
if ( !increment_ulong(&v_end, round_pgup(nr_pages * sizeof(long))) )
goto error_out;
@@ -845,31 +882,8 @@ static int setup_guest(int xc_handle,
goto error_out;
}
- if ( (page_array = malloc(nr_pages * sizeof(unsigned long))) == NULL )
- {
- PERROR("Could not allocate memory");
- goto error_out;
- }
-
- if ( xc_get_pfn_list(xc_handle, dom, page_array, nr_pages) != nr_pages )
- {
- PERROR("Could not get the page frame list");
- goto error_out;
- }
-
- rc = (load_funcs.loadimage)(image, image_size,
- xc_handle, dom, page_array,
- &dsi);
- if ( rc != 0 )
- goto error_out;
-
- if ( load_initrd(xc_handle, dom, initrd,
- vinitrd_start - dsi.v_start, page_array) )
- goto error_out;
-
- /* setup page tables */
#if defined(__i386__)
- if (dsi.pae_kernel != PAEKERN_no)
+ if ( dsi.pae_kernel != PAEKERN_no )
rc = setup_pg_tables_pae(xc_handle, dom, ctxt,
dsi.v_start, v_end,
page_array, vpt_start, vpt_end,
@@ -886,16 +900,16 @@ static int setup_guest(int xc_handle,
page_array, vpt_start, vpt_end,
shadow_mode_enabled);
#endif
- if (0 != rc)
+ if ( rc != 0 )
goto error_out;
-#if defined(__i386__)
/*
* Pin down l2tab addr as page dir page - causes hypervisor to provide
* correct protection for the page
*/
if ( !shadow_mode_enabled )
{
+#if defined(__i386__)
if ( dsi.pae_kernel != PAEKERN_no )
{
if ( pin_table(xc_handle, MMUEXT_PIN_L3_TABLE,
@@ -908,40 +922,24 @@ static int setup_guest(int xc_handle,
xen_cr3_to_pfn(ctxt->ctrlreg[3]), dom) )
goto error_out;
}
- }
-#endif
-
-#if defined(__x86_64__)
- /*
- * Pin down l4tab addr as page dir page - causes hypervisor to provide
- * correct protection for the page
- */
- if ( pin_table(xc_handle, MMUEXT_PIN_L4_TABLE,
- xen_cr3_to_pfn(ctxt->ctrlreg[3]), dom) )
- goto error_out;
+#elif defined(__x86_64__)
+ /*
+ * Pin down l4tab addr as page dir page - causes hypervisor to provide
+ * correct protection for the page
+ */
+ if ( pin_table(xc_handle, MMUEXT_PIN_L4_TABLE,
+ xen_cr3_to_pfn(ctxt->ctrlreg[3]), dom) )
+ goto error_out;
#endif
+ }
- if ( (mmu = xc_init_mmu_updates(xc_handle, dom)) == NULL )
- goto error_out;
-
- /* Write the phys->machine and machine->phys table entries. */
+ /* Write the phys->machine table entries (machine->phys already done). */
physmap_pfn = (vphysmap_start - dsi.v_start) >> PAGE_SHIFT;
physmap = physmap_e = xc_map_foreign_range(
xc_handle, dom, PAGE_SIZE, PROT_READ|PROT_WRITE,
page_array[physmap_pfn++]);
-
for ( count = 0; count < nr_pages; count++ )
{
- if ( xc_add_mmu_update(
- xc_handle, mmu,
- ((uint64_t)page_array[count] << PAGE_SHIFT) | MMU_MACHPHYS_UPDATE,
- count) )
- {
- DPRINTF("m2p update failure p=%lx m=%"PRIx64"\n",
- count, (uint64_t)page_array[count]);
- munmap(physmap, PAGE_SIZE);
- goto error_out;
- }
*physmap_e++ = page_array[count];
if ( ((unsigned long)physmap_e & (PAGE_SIZE-1)) == 0 )
{
@@ -953,10 +951,6 @@ static int setup_guest(int xc_handle,
}
munmap(physmap, PAGE_SIZE);
- /* Send the page update requests down to the hypervisor. */
- if ( xc_finish_mmu_updates(xc_handle, mmu) )
- goto error_out;
-
if ( shadow_mode_enabled )
{
struct xen_add_to_physmap xatp;
@@ -1063,10 +1057,6 @@ static int setup_guest(int xc_handle,
munmap(shared_info, PAGE_SIZE);
- /* Send the page update requests down to the hypervisor. */
- if ( xc_finish_mmu_updates(xc_handle, mmu) )
- goto error_out;
-
hypercall_page = xen_elfnote_numeric(&dsi, XEN_ELFNOTE_HYPERCALL_PAGE,
&hypercall_page_defined);
if ( hypercall_page_defined )
@@ -1082,7 +1072,6 @@ static int setup_guest(int xc_handle,
goto error_out;
}
- free(mmu);
free(page_array);
*pvsi = vstartinfo_start;
@@ -1092,7 +1081,6 @@ static int setup_guest(int xc_handle,
return 0;
error_out:
- free(mmu);
free(page_array);
return -1;
}
@@ -1100,6 +1088,7 @@ static int setup_guest(int xc_handle,
static int xc_linux_build_internal(int xc_handle,
uint32_t domid,
+ unsigned int mem_mb,
char *image,
unsigned long image_size,
struct initrd_info *initrd,
@@ -1114,8 +1103,7 @@ static int xc_linux_build_internal(int xc_handle,
struct xen_domctl launch_domctl;
DECLARE_DOMCTL;
int rc, i;
- vcpu_guest_context_t st_ctxt, *ctxt = &st_ctxt;
- unsigned long nr_pages;
+ struct vcpu_guest_context st_ctxt, *ctxt = &st_ctxt;
unsigned long vstartinfo_start, vkern_entry, vstack_start;
uint32_t features_bitmap[XENFEAT_NR_SUBMAPS] = { 0, };
@@ -1128,12 +1116,6 @@ static int xc_linux_build_internal(int xc_handle,
}
}
- if ( (nr_pages = get_tot_pages(xc_handle, domid)) < 0 )
- {
- PERROR("Could not find total pages for domain");
- goto error_out;
- }
-
#ifdef VALGRIND
memset(&st_ctxt, 0, sizeof(st_ctxt));
#endif
@@ -1157,7 +1139,7 @@ static int xc_linux_build_internal(int xc_handle,
if ( setup_guest(xc_handle, domid, image, image_size,
initrd,
- nr_pages,
+ mem_mb << (20 - PAGE_SHIFT),
&vstartinfo_start, &vkern_entry,
&vstack_start, ctxt, cmdline,
domctl.u.getdomaininfo.shared_info_frame,
@@ -1253,6 +1235,7 @@ static int xc_linux_build_internal(int xc_handle,
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,
@@ -1301,7 +1284,7 @@ int xc_linux_build_mem(int xc_handle,
}
}
- sts = xc_linux_build_internal(xc_handle, domid, img_buf, img_len,
+ sts = xc_linux_build_internal(xc_handle, domid, mem_mb, img_buf, img_len,
&initrd_info, cmdline, features, flags,
store_evtchn, store_mfn,
console_evtchn, console_mfn);
@@ -1321,6 +1304,7 @@ int xc_linux_build_mem(int xc_handle,
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,
@@ -1350,7 +1334,6 @@ int xc_linux_build(int xc_handle,
goto error_out;
}
- initrd_info.len = xc_get_filesz(fd);
if ( (initrd_info.u.file_handle = gzdopen(fd, "rb")) == NULL )
{
PERROR("Could not allocate decompression state for initrd");
@@ -1358,7 +1341,7 @@ int xc_linux_build(int xc_handle,
}
}
- sts = xc_linux_build_internal(xc_handle, domid, image, image_size,
+ sts = xc_linux_build_internal(xc_handle, domid, mem_mb, image, image_size,
&initrd_info, cmdline, features, flags,
store_evtchn, store_mfn,
console_evtchn, console_mfn);
diff --git a/tools/libxc/xc_linux_save.c b/tools/libxc/xc_linux_save.c
index 7a5e4eaad6..a38f80c7fe 100644
--- a/tools/libxc/xc_linux_save.c
+++ b/tools/libxc/xc_linux_save.c
@@ -978,12 +978,14 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
}
if(!write_exact(io_fd, &batch, sizeof(unsigned int))) {
- ERROR("Error when writing to state file (2)");
+ ERROR("Error when writing to state file (2) (errno %d)",
+ errno);
goto out;
}
if(!write_exact(io_fd, pfn_type, sizeof(unsigned long)*j)) {
- ERROR("Error when writing to state file (3)");
+ ERROR("Error when writing to state file (3) (errno %d)",
+ errno);
goto out;
}
@@ -1013,7 +1015,8 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
goto out;
if (ratewrite(io_fd, page, PAGE_SIZE) != PAGE_SIZE) {
- ERROR("Error when writing to state file (4)");
+ ERROR("Error when writing to state file (4)"
+ " (errno %d)", errno);
goto out;
}
@@ -1021,7 +1024,8 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
/* We have a normal page: just write it directly. */
if (ratewrite(io_fd, spage, PAGE_SIZE) != PAGE_SIZE) {
- ERROR("Error when writing to state file (5)");
+ ERROR("Error when writing to state file (5)"
+ " (errno %d)", errno);
goto out;
}
}
@@ -1056,7 +1060,8 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
/* send "-1" to put receiver into debug mode */
if(!write_exact(io_fd, &minusone, sizeof(int))) {
- ERROR("Error when writing to state file (6)");
+ ERROR("Error when writing to state file (6) (errno %d)",
+ errno);
goto out;
}
@@ -1110,7 +1115,7 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
/* Zero terminate */
i = 0;
if (!write_exact(io_fd, &i, sizeof(int))) {
- ERROR("Error when writing to state file (6)");
+ ERROR("Error when writing to state file (6') (errno %d)", errno);
goto out;
}
@@ -1125,7 +1130,7 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
}
if(!write_exact(io_fd, &j, sizeof(unsigned int))) {
- ERROR("Error when writing to state file (6a)");
+ ERROR("Error when writing to state file (6a) (errno %d)", errno);
goto out;
}
@@ -1137,7 +1142,8 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
i++;
if (j == 1024 || i == max_pfn) {
if(!write_exact(io_fd, &pfntab, sizeof(unsigned long)*j)) {
- ERROR("Error when writing to state file (6b)");
+ ERROR("Error when writing to state file (6b) (errno %d)",
+ errno);
goto out;
}
j = 0;
@@ -1170,7 +1176,7 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
if (!write_exact(io_fd, &ctxt, sizeof(ctxt)) ||
!write_exact(io_fd, live_shinfo, PAGE_SIZE)) {
- ERROR("Error when writing to state file (1)");
+ ERROR("Error when writing to state file (1) (errno %d)", errno);
goto out;
}
diff --git a/tools/libxc/xc_misc.c b/tools/libxc/xc_misc.c
index 37c505e2bc..b1924611c3 100644
--- a/tools/libxc/xc_misc.c
+++ b/tools/libxc/xc_misc.c
@@ -5,6 +5,7 @@
*/
#include "xc_private.h"
+#include <xen/hvm/hvm_op.h>
int xc_readconsolering(int xc_handle,
char **pbuffer,
@@ -89,6 +90,33 @@ int xc_perfc_control(int xc_handle,
return rc;
}
+int xc_hvm_set_irq_level(int xc_handle, domid_t dom, int irq, int level)
+{
+ DECLARE_HYPERCALL;
+ struct xen_hvm_set_irq_level arg;
+ int rc;
+
+ hypercall.op = __HYPERVISOR_hvm_op;
+ hypercall.arg[0] = HVMOP_set_irq_level;
+ hypercall.arg[1] = (unsigned long)&arg;
+
+ arg.domid = dom;
+ arg.irq = irq;
+ arg.level = level;
+
+ if ( mlock(&arg, sizeof(arg)) != 0 )
+ {
+ PERROR("Could not lock memory");
+ return -1;
+ }
+
+ rc = do_xen_hypercall(xc_handle, &hypercall);
+
+ safe_munlock(&arg, sizeof(arg));
+
+ return rc;
+}
+
/*
* Local variables:
* mode: C
diff --git a/tools/libxc/xc_private.c b/tools/libxc/xc_private.c
index 768cf5c5cf..a2600fdc1b 100644
--- a/tools/libxc/xc_private.c
+++ b/tools/libxc/xc_private.c
@@ -344,28 +344,6 @@ int xc_clear_domain_page(int xc_handle,
return 0;
}
-unsigned long xc_get_filesz(int fd)
-{
- uint16_t sig;
- uint32_t _sz = 0;
- unsigned long sz;
-
- lseek(fd, 0, SEEK_SET);
- if ( read(fd, &sig, sizeof(sig)) != sizeof(sig) )
- return 0;
- sz = lseek(fd, 0, SEEK_END);
- if ( sig == 0x8b1f ) /* GZIP signature? */
- {
- lseek(fd, -4, SEEK_END);
- if ( read(fd, &_sz, 4) != 4 )
- return 0;
- sz = _sz;
- }
- lseek(fd, 0, SEEK_SET);
-
- return sz;
-}
-
void xc_map_memcpy(unsigned long dst, const char *src, unsigned long size,
int xch, uint32_t dom, xen_pfn_t *parray,
unsigned long vstart)
diff --git a/tools/libxc/xc_private.h b/tools/libxc/xc_private.h
index 73c870bdb7..20f7a9b445 100644
--- a/tools/libxc/xc_private.h
+++ b/tools/libxc/xc_private.h
@@ -158,4 +158,9 @@ static inline int do_sysctl(int xc_handle, struct xen_sysctl *sysctl)
int xc_map_foreign_ranges(int xc_handle, uint32_t dom,
privcmd_mmap_entry_t *entries, int nr);
+void *map_domain_va_core(unsigned long domfd, int cpu, void *guest_va,
+ vcpu_guest_context_t *ctxt);
+int xc_waitdomain_core(int xc_handle, int domain, int *status,
+ int options, vcpu_guest_context_t *ctxt);
+
#endif /* __XC_PRIVATE_H__ */
diff --git a/tools/libxc/xc_ptrace.c b/tools/libxc/xc_ptrace.c
index 2525417f16..66bbf9539b 100644
--- a/tools/libxc/xc_ptrace.c
+++ b/tools/libxc/xc_ptrace.c
@@ -36,8 +36,9 @@ static char *ptrace_names[] = {
};
#endif
-static int current_domid = -1;
-static int current_isfile;
+static int current_domid = -1;
+static int current_isfile;
+static int current_is_hvm;
static uint64_t online_cpumap;
static uint64_t regs_valid;
@@ -46,7 +47,6 @@ static vcpu_guest_context_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)
{
@@ -172,7 +172,7 @@ to_ma(int cpu,
{
unsigned long maddr = in_addr;
- if ( (ctxt[cpu].flags & VGCF_HVM_GUEST) && paging_enabled(&ctxt[cpu]) )
+ if ( current_is_hvm && paging_enabled(&ctxt[cpu]) )
maddr = page_array[maddr >> PAGE_SHIFT] << PAGE_SHIFT;
return maddr;
}
@@ -443,7 +443,7 @@ __xc_waitdomain(
goto done;
}
- if ( !(domctl.u.getdomaininfo.flags & DOMFLAGS_PAUSED) )
+ if ( !(domctl.u.getdomaininfo.flags & XEN_DOMINF_paused) )
{
nanosleep(&ts,NULL);
goto retry;
@@ -482,11 +482,11 @@ xc_ptrace(
case PTRACE_PEEKTEXT:
case PTRACE_PEEKDATA:
if (current_isfile)
- guest_va = (unsigned long *)map_domain_va_core(current_domid,
- cpu, addr, ctxt);
+ 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);
+ guest_va = (unsigned long *)map_domain_va(
+ xc_handle, cpu, addr, PROT_READ);
if ( guest_va == NULL )
goto out_error;
retval = *guest_va;
@@ -496,11 +496,11 @@ xc_ptrace(
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);
+ 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);
+ 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;
@@ -590,10 +590,11 @@ xc_ptrace(
retval = do_domctl(xc_handle, &domctl);
if ( retval || (domctl.domain != current_domid) )
goto out_error_domctl;
- if ( domctl.u.getdomaininfo.flags & DOMFLAGS_PAUSED )
+ 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);
domctl.cmd = XEN_DOMCTL_setdebugging;
domctl.domain = current_domid;
domctl.u.setdebugging.enable = 1;
diff --git a/tools/libxc/xc_ptrace_core.c b/tools/libxc/xc_ptrace_core.c
index 03bbf318a3..3ae69d3ac2 100644
--- a/tools/libxc/xc_ptrace_core.c
+++ b/tools/libxc/xc_ptrace_core.c
@@ -7,6 +7,7 @@
/* XXX application state */
+static int current_is_hvm = 0;
static long nr_pages = 0;
static unsigned long *p2m_array = NULL;
static unsigned long *m2p_array = NULL;
@@ -24,8 +25,8 @@ map_mtop_offset(unsigned long ma)
void *
-map_domain_va_core(unsigned long domfd, int cpu, void * guest_va,
- vcpu_guest_context_t *ctxt)
+map_domain_va_core(unsigned long domfd, int cpu, void *guest_va,
+ vcpu_guest_context_t *ctxt)
{
unsigned long pde, page;
unsigned long va = (unsigned long)guest_va;
@@ -55,7 +56,7 @@ map_domain_va_core(unsigned long domfd, int cpu, void * guest_va,
}
if ((pde = cr3_virt[cpu][l2_table_offset_i386(va)]) == 0) /* logical address */
return NULL;
- if (ctxt[cpu].flags & VGCF_HVM_GUEST)
+ if (current_is_hvm)
pde = p2m_array[pde >> PAGE_SHIFT] << PAGE_SHIFT;
if (pde != pde_phys[cpu])
{
@@ -71,7 +72,7 @@ map_domain_va_core(unsigned long domfd, int cpu, void * guest_va,
}
if ((page = pde_virt[cpu][l1_table_offset_i386(va)]) == 0) /* logical address */
return NULL;
- if (ctxt[cpu].flags & VGCF_HVM_GUEST)
+ if (current_is_hvm)
page = p2m_array[page >> PAGE_SHIFT] << PAGE_SHIFT;
if (page != page_phys[cpu])
{
@@ -104,17 +105,18 @@ xc_waitdomain_core(
int i;
xc_core_header_t header;
- if (nr_pages == 0)
+ if ( nr_pages == 0 )
{
-
if (read(domfd, &header, sizeof(header)) != sizeof(header))
return -1;
- if (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;
+ current_is_hvm = (header.xch_magic == XC_CORE_MAGIC_HVM);
+ if ( !current_is_hvm && (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 = header.xch_nr_pages;
diff --git a/tools/libxc/xenctrl.h b/tools/libxc/xenctrl.h
index d4249936c9..d5029407dc 100644
--- a/tools/libxc/xenctrl.h
+++ b/tools/libxc/xenctrl.h
@@ -47,10 +47,9 @@
#define rmb() __asm__ __volatile__ ( "lfence" : : : "memory")
#define wmb() __asm__ __volatile__ ( "" : : : "memory")
#elif defined(__ia64__)
-/* FIXME */
-#define mb()
-#define rmb()
-#define wmb()
+#define mb() __asm__ __volatile__ ("mf" ::: "memory")
+#define rmb() __asm__ __volatile__ ("mf" ::: "memory")
+#define wmb() __asm__ __volatile__ ("mf" ::: "memory")
#elif defined(__powerpc__)
/* XXX loosen these up later */
#define mb() __asm__ __volatile__ ("sync" : : : "memory")
@@ -113,26 +112,14 @@ typedef struct xc_core_header {
unsigned int xch_pages_offset;
} xc_core_header_t;
-#define XC_CORE_MAGIC 0xF00FEBED
+#define XC_CORE_MAGIC 0xF00FEBED
+#define XC_CORE_MAGIC_HVM 0xF00FEBEE
#ifdef __linux__
#include <sys/ptrace.h>
#include <thread_db.h>
-void * map_domain_va_core(
- unsigned long domfd,
- int cpu,
- void *guest_va,
- vcpu_guest_context_t *ctxt);
-
-int xc_waitdomain_core(
- int xc_handle,
- int domain,
- int *status,
- int options,
- vcpu_guest_context_t *ctxt);
-
typedef void (*thr_ev_handler_t)(long);
void xc_register_event_handler(
@@ -158,11 +145,12 @@ int xc_waitdomain(
* DOMAIN MANAGEMENT FUNCTIONS
*/
-typedef struct {
+typedef struct xc_dominfo {
uint32_t domid;
uint32_t ssidref;
unsigned int dying:1, crashed:1, shutdown:1,
- paused:1, blocked:1, running:1;
+ paused:1, blocked:1, running:1,
+ hvm:1;
unsigned int shutdown_reason; /* only meaningful if shutdown==1 */
unsigned long nr_pages;
unsigned long shared_info_frame;
@@ -177,6 +165,7 @@ typedef xen_domctl_getdomaininfo_t xc_domaininfo_t;
int xc_domain_create(int xc_handle,
uint32_t ssidref,
xen_domain_handle_t handle,
+ uint32_t flags,
uint32_t *pdomid);
@@ -677,4 +666,6 @@ evtchn_port_t xc_evtchn_pending(int xce_handle);
*/
int xc_evtchn_unmask(int xce_handle, evtchn_port_t port);
+int xc_hvm_set_irq_level(int xce_handle, domid_t dom, int irq, int level);
+
#endif
diff --git a/tools/libxc/xenguest.h b/tools/libxc/xenguest.h
index 948c03b509..d6343fbf5c 100644
--- a/tools/libxc/xenguest.h
+++ b/tools/libxc/xenguest.h
@@ -48,8 +48,9 @@ int xc_linux_restore(int xc_handle, int io_fd, uint32_t dom,
*
* @parm xc_handle a handle to an open hypervisor interface
* @parm domid the id of the domain
- * @param image_name name of the kernel image file
- * @param ramdisk_name name of the ramdisk image file
+ * @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
@@ -60,6 +61,7 @@ int xc_linux_restore(int xc_handle, int io_fd, uint32_t dom,
*/
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,
@@ -74,22 +76,24 @@ int xc_linux_build(int xc_handle,
* This function will create a domain for a paravirtualized Linux
* using buffers for kernel and initrd
*
- * @param xc_handle a handle to an open hypervisor interface
- * @param domid the id of the domain
- * @param image_buffer buffer containing kernel image
- * @param image_size size of the kernel image buffer
- * @param initrd_buffer name of the ramdisk image file
- * @param initrd_size size of the ramdisk buffer
- * @param cmdline command line string
- * @param flags domain creation flags
- * @param store_evtchn the store event channel for this domain to use
- * @param store_mfn returned with the mfn of the store page
- * @param console_evtchn the console event channel for this domain to use
- * @param conole_mfn returned with the mfn of the console page
+ * @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,
@@ -109,7 +113,6 @@ int xc_hvm_build(int xc_handle,
unsigned int vcpus,
unsigned int pae,
unsigned int acpi,
- unsigned int apic,
unsigned int store_evtchn,
unsigned long *store_mfn);
@@ -121,7 +124,6 @@ int xc_hvm_build_mem(int xc_handle,
unsigned int vcpus,
unsigned int pae,
unsigned int acpi,
- unsigned int apic,
unsigned int store_evtchn,
unsigned long *store_mfn);
diff --git a/tools/libxc/xg_private.c b/tools/libxc/xg_private.c
index 86cef6efa8..49fcb624e8 100644
--- a/tools/libxc/xg_private.c
+++ b/tools/libxc/xg_private.c
@@ -31,7 +31,7 @@ char *xc_read_image(const char *filename, unsigned long *size)
{
int kernel_fd = -1;
gzFile kernel_gfd = NULL;
- char *image = NULL;
+ char *image = NULL, *tmp;
unsigned int bytes;
if ( (filename == NULL) || (size == NULL) )
@@ -43,33 +43,58 @@ char *xc_read_image(const char *filename, unsigned long *size)
goto out;
}
- if ( (*size = xc_get_filesz(kernel_fd)) == 0 )
- {
- PERROR("Could not read kernel image");
- goto out;
- }
-
if ( (kernel_gfd = gzdopen(kernel_fd, "rb")) == NULL )
{
PERROR("Could not allocate decompression state for state file");
goto out;
}
- if ( (image = malloc(*size)) == NULL )
+ *size = 0;
+
+#define CHUNK 1*1024*1024
+ while(1)
{
- PERROR("Could not allocate memory for kernel image");
- goto out;
+ 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
- if ( (bytes = gzread(kernel_gfd, image, *size)) != *size )
+ out:
+ if ( *size == 0 )
{
- PERROR("Error reading kernel image, could not"
- " read the whole image (%d != %ld).", bytes, *size);
- free(image);
- image = NULL;
+ 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;
}
- out:
if ( kernel_gfd != NULL )
gzclose(kernel_gfd);
else if ( kernel_fd >= 0 )
@@ -171,7 +196,6 @@ __attribute__((weak)) int xc_hvm_build(
unsigned int vcpus,
unsigned int pae,
unsigned int acpi,
- unsigned int apic,
unsigned int store_evtchn,
unsigned long *store_mfn)
{
diff --git a/tools/libxc/xg_private.h b/tools/libxc/xg_private.h
index 63ad2bcfdc..64ac8fad48 100644
--- a/tools/libxc/xg_private.h
+++ b/tools/libxc/xg_private.h
@@ -193,8 +193,6 @@ typedef struct mfn_mapper {
int xc_copy_to_domain_page(int xc_handle, uint32_t domid,
unsigned long dst_pfn, const char *src_page);
-unsigned long xc_get_filesz(int fd);
-
void xc_map_memcpy(unsigned long dst, const char *src, unsigned long size,
int xch, uint32_t dom, xen_pfn_t *parray,
unsigned long vstart);
diff --git a/tools/libxen/COPYING b/tools/libxen/COPYING
new file mode 100644
index 0000000000..b124cf5812
--- /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.
+
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, 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.
+
+ <signature of Ty Coon>, 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 0000000000..88755ab6dc
--- /dev/null
+++ b/tools/libxen/Makefile
@@ -0,0 +1,37 @@
+#
+# 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
+#
+
+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)
+
+test/test_bindings: test/test_bindings.o src/libxen.so
+ $(CC) $(LDFLAGS) -o $@ $< -L src -lxen
+
+src/libxen.so: $(patsubst %.c, %.o, $(wildcard src/*.c))
+ $(CC) -shared -o $@ $^
+
+.PHONY: clean
+clean:
+ rm -f `find -name *.o`
+ rm -f src/libxen.so
+ rm -f test/test_bindings
diff --git a/tools/libxen/README b/tools/libxen/README
new file mode 100644
index 0000000000..4aa9450381
--- /dev/null
+++ b/tools/libxen/README
@@ -0,0 +1,54 @@
+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 run the test, do
+
+LD_LIBRARY_PATH=src ./test/test_bindings <url> <username> <password>
+
+where <url> is the fragment of the server URL that follows the http://, for
+example "localhost:8005/RPC2".
diff --git a/tools/libxen/include/xen_boot_type.h b/tools/libxen/include/xen_boot_type.h
new file mode 100644
index 0000000000..8d3bc03588
--- /dev/null
+++ b/tools/libxen/include/xen_boot_type.h
@@ -0,0 +1,87 @@
+/*
+ * 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_BOOT_TYPE_H
+#define XEN_BOOT_TYPE_H
+
+
+#include "xen_common.h"
+
+
+enum xen_boot_type
+{
+ /**
+ * boot an HVM guest using an emulated BIOS
+ */
+ XEN_BOOT_TYPE_BIOS,
+
+ /**
+ * boot from inside the machine using grub
+ */
+ XEN_BOOT_TYPE_GRUB,
+
+ /**
+ * boot from an external kernel
+ */
+ XEN_BOOT_TYPE_KERNEL_EXTERNAL,
+
+ /**
+ * boot from a kernel inside the guest filesystem
+ */
+ XEN_BOOT_TYPE_KERNEL_INTERNAL
+};
+
+
+typedef struct xen_boot_type_set
+{
+ size_t size;
+ enum xen_boot_type contents[];
+} xen_boot_type_set;
+
+/**
+ * Allocate a xen_boot_type_set of the given size.
+ */
+extern xen_boot_type_set *
+xen_boot_type_set_alloc(size_t size);
+
+/**
+ * Free the given xen_boot_type_set. The given set must have been
+ * allocated by this library.
+ */
+extern void
+xen_boot_type_set_free(xen_boot_type_set *set);
+
+
+/**
+ * Return the name corresponding to the given code. This string must
+ * not be modified or freed.
+ */
+extern const char *
+xen_boot_type_to_string(enum xen_boot_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_boot_type
+xen_boot_type_from_string(xen_session *session, const char *str);
+
+
+#endif
diff --git a/tools/libxen/include/xen_boot_type_internal.h b/tools/libxen/include/xen_boot_type_internal.h
new file mode 100644
index 0000000000..968dc4a9ed
--- /dev/null
+++ b/tools/libxen/include/xen_boot_type_internal.h
@@ -0,0 +1,37 @@
+/*
+ * 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
+ */
+
+
+/*
+ * Declarations of the abstract types used during demarshalling of enum
+ * xen_boot_type. Internal to this library -- do not use from outside.
+ */
+
+
+#ifndef XEN_BOOT_TYPE_INTERNAL_H
+#define XEN_BOOT_TYPE_INTERNAL_H
+
+
+#include "xen_internal.h"
+
+
+extern const abstract_type xen_boot_type_abstract_type_;
+extern const abstract_type xen_boot_type_set_abstract_type_;
+
+
+#endif
diff --git a/tools/libxen/include/xen_common.h b/tools/libxen/include/xen_common.h
new file mode 100644
index 0000000000..9f157bc5b3
--- /dev/null
+++ b/tools/libxen/include/xen_common.h
@@ -0,0 +1,145 @@
+/*
+ * 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 <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <time.h>
+
+#include "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;
+
+
+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();
+
+
+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);
+
+
+/**
+ * Set *result to be a handle to the host to which this session is connected.
+ */
+extern int
+xen_session_get_this_host(xen_session *session, xen_host *result);
+
+
+#endif
diff --git a/tools/libxen/include/xen_cpu_feature.h b/tools/libxen/include/xen_cpu_feature.h
new file mode 100644
index 0000000000..6e0614a8ac
--- /dev/null
+++ b/tools/libxen/include/xen_cpu_feature.h
@@ -0,0 +1,387 @@
+/*
+ * 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_CPU_FEATURE_H
+#define XEN_CPU_FEATURE_H
+
+
+#include "xen_common.h"
+
+
+enum xen_cpu_feature
+{
+ /**
+ * Onboard FPU
+ */
+ XEN_CPU_FEATURE_FPU,
+
+ /**
+ * Virtual Mode Extensions
+ */
+ XEN_CPU_FEATURE_VME,
+
+ /**
+ * Debugging Extensions
+ */
+ XEN_CPU_FEATURE_DE,
+
+ /**
+ * Page Size Extensions
+ */
+ XEN_CPU_FEATURE_PSE,
+
+ /**
+ * Time Stamp Counter
+ */
+ XEN_CPU_FEATURE_TSC,
+
+ /**
+ * Model-Specific Registers, RDMSR, WRMSR
+ */
+ XEN_CPU_FEATURE_MSR,
+
+ /**
+ * Physical Address Extensions
+ */
+ XEN_CPU_FEATURE_PAE,
+
+ /**
+ * Machine Check Architecture
+ */
+ XEN_CPU_FEATURE_MCE,
+
+ /**
+ * CMPXCHG8 instruction
+ */
+ XEN_CPU_FEATURE_CX8,
+
+ /**
+ * Onboard APIC
+ */
+ XEN_CPU_FEATURE_APIC,
+
+ /**
+ * SYSENTER/SYSEXIT
+ */
+ XEN_CPU_FEATURE_SEP,
+
+ /**
+ * Memory Type Range Registers
+ */
+ XEN_CPU_FEATURE_MTRR,
+
+ /**
+ * Page Global Enable
+ */
+ XEN_CPU_FEATURE_PGE,
+
+ /**
+ * Machine Check Architecture
+ */
+ XEN_CPU_FEATURE_MCA,
+
+ /**
+ * CMOV instruction (FCMOVCC and FCOMI too if FPU present)
+ */
+ XEN_CPU_FEATURE_CMOV,
+
+ /**
+ * Page Attribute Table
+ */
+ XEN_CPU_FEATURE_PAT,
+
+ /**
+ * 36-bit PSEs
+ */
+ XEN_CPU_FEATURE_PSE36,
+
+ /**
+ * Processor serial number
+ */
+ XEN_CPU_FEATURE_PN,
+
+ /**
+ * Supports the CLFLUSH instruction
+ */
+ XEN_CPU_FEATURE_CLFLSH,
+
+ /**
+ * Debug Trace Store
+ */
+ XEN_CPU_FEATURE_DTES,
+
+ /**
+ * ACPI via MSR
+ */
+ XEN_CPU_FEATURE_ACPI,
+
+ /**
+ * Multimedia Extensions
+ */
+ XEN_CPU_FEATURE_MMX,
+
+ /**
+ * FXSAVE and FXRSTOR instructions (fast save and restore
+ */
+ XEN_CPU_FEATURE_FXSR,
+
+ /**
+ * Streaming SIMD Extensions
+ */
+ XEN_CPU_FEATURE_XMM,
+
+ /**
+ * Streaming SIMD Extensions-2
+ */
+ XEN_CPU_FEATURE_XMM2,
+
+ /**
+ * CPU self snoop
+ */
+ XEN_CPU_FEATURE_SELFSNOOP,
+
+ /**
+ * Hyper-Threading
+ */
+ XEN_CPU_FEATURE_HT,
+
+ /**
+ * Automatic clock control
+ */
+ XEN_CPU_FEATURE_ACC,
+
+ /**
+ * IA-64 processor
+ */
+ XEN_CPU_FEATURE_IA64,
+
+ /**
+ * SYSCALL/SYSRET
+ */
+ XEN_CPU_FEATURE_SYSCALL,
+
+ /**
+ * MP Capable.
+ */
+ XEN_CPU_FEATURE_MP,
+
+ /**
+ * Execute Disable
+ */
+ XEN_CPU_FEATURE_NX,
+
+ /**
+ * AMD MMX extensions
+ */
+ XEN_CPU_FEATURE_MMXEXT,
+
+ /**
+ * Long Mode (x86-64)
+ */
+ XEN_CPU_FEATURE_LM,
+
+ /**
+ * AMD 3DNow! extensions
+ */
+ XEN_CPU_FEATURE_3DNOWEXT,
+
+ /**
+ * 3DNow!
+ */
+ XEN_CPU_FEATURE_3DNOW,
+
+ /**
+ * CPU in recovery mode
+ */
+ XEN_CPU_FEATURE_RECOVERY,
+
+ /**
+ * Longrun power control
+ */
+ XEN_CPU_FEATURE_LONGRUN,
+
+ /**
+ * LongRun table interface
+ */
+ XEN_CPU_FEATURE_LRTI,
+
+ /**
+ * Cyrix MMX extensions
+ */
+ XEN_CPU_FEATURE_CXMMX,
+
+ /**
+ * AMD K6 nonstandard MTRRs
+ */
+ XEN_CPU_FEATURE_K6_MTRR,
+
+ /**
+ * Cyrix ARRs (= MTRRs)
+ */
+ XEN_CPU_FEATURE_CYRIX_ARR,
+
+ /**
+ * Centaur MCRs (= MTRRs)
+ */
+ XEN_CPU_FEATURE_CENTAUR_MCR,
+
+ /**
+ * Opteron, Athlon64
+ */
+ XEN_CPU_FEATURE_K8,
+
+ /**
+ * Athlon
+ */
+ XEN_CPU_FEATURE_K7,
+
+ /**
+ * P3
+ */
+ XEN_CPU_FEATURE_P3,
+
+ /**
+ * P4
+ */
+ XEN_CPU_FEATURE_P4,
+
+ /**
+ * TSC ticks at a constant rate
+ */
+ XEN_CPU_FEATURE_CONSTANT_TSC,
+
+ /**
+ * FXSAVE leaks FOP/FIP/FOP
+ */
+ XEN_CPU_FEATURE_FXSAVE_LEAK,
+
+ /**
+ * Streaming SIMD Extensions-3
+ */
+ XEN_CPU_FEATURE_XMM3,
+
+ /**
+ * Monitor/Mwait support
+ */
+ XEN_CPU_FEATURE_MWAIT,
+
+ /**
+ * CPL Qualified Debug Store
+ */
+ XEN_CPU_FEATURE_DSCPL,
+
+ /**
+ * Enhanced SpeedStep
+ */
+ XEN_CPU_FEATURE_EST,
+
+ /**
+ * Thermal Monitor 2
+ */
+ XEN_CPU_FEATURE_TM2,
+
+ /**
+ * Context ID
+ */
+ XEN_CPU_FEATURE_CID,
+
+ /**
+ * CMPXCHG16B
+ */
+ XEN_CPU_FEATURE_CX16,
+
+ /**
+ * Send Task Priority Messages
+ */
+ XEN_CPU_FEATURE_XTPR,
+
+ /**
+ * on-CPU RNG present (xstore insn)
+ */
+ XEN_CPU_FEATURE_XSTORE,
+
+ /**
+ * on-CPU RNG enabled
+ */
+ XEN_CPU_FEATURE_XSTORE_EN,
+
+ /**
+ * on-CPU crypto (xcrypt insn)
+ */
+ XEN_CPU_FEATURE_XCRYPT,
+
+ /**
+ * on-CPU crypto enabled
+ */
+ XEN_CPU_FEATURE_XCRYPT_EN,
+
+ /**
+ * LAHF/SAHF in long mode
+ */
+ XEN_CPU_FEATURE_LAHF_LM,
+
+ /**
+ * If yes HyperThreading not valid
+ */
+ XEN_CPU_FEATURE_CMP_LEGACY,
+
+ /**
+ * VMX instruction set
+ */
+ XEN_CPU_FEATURE_VMX
+};
+
+
+typedef struct xen_cpu_feature_set
+{
+ size_t size;
+ enum xen_cpu_feature contents[];
+} xen_cpu_feature_set;
+
+/**
+ * Allocate a xen_cpu_feature_set of the given size.
+ */
+extern xen_cpu_feature_set *
+xen_cpu_feature_set_alloc(size_t size);
+
+/**
+ * Free the given xen_cpu_feature_set. The given set must have been
+ * allocated by this library.
+ */
+extern void
+xen_cpu_feature_set_free(xen_cpu_feature_set *set);
+
+
+/**
+ * Return the name corresponding to the given code. This string must
+ * not be modified or freed.
+ */
+extern const char *
+xen_cpu_feature_to_string(enum xen_cpu_feature 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_cpu_feature
+xen_cpu_feature_from_string(xen_session *session, const char *str);
+
+
+#endif
diff --git a/tools/libxen/include/xen_cpu_feature_internal.h b/tools/libxen/include/xen_cpu_feature_internal.h
new file mode 100644
index 0000000000..c4616a7c5f
--- /dev/null
+++ b/tools/libxen/include/xen_cpu_feature_internal.h
@@ -0,0 +1,37 @@
+/*
+ * 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
+ */
+
+
+/*
+ * Declarations of the abstract types used during demarshalling of enum
+ * xen_cpu_feature. Internal to this library -- do not use from outside.
+ */
+
+
+#ifndef XEN_CPU_FEATURE_INTERNAL_H
+#define XEN_CPU_FEATURE_INTERNAL_H
+
+
+#include "xen_internal.h"
+
+
+extern const abstract_type xen_cpu_feature_abstract_type_;
+extern const abstract_type xen_cpu_feature_set_abstract_type_;
+
+
+#endif
diff --git a/tools/libxen/include/xen_driver_type.h b/tools/libxen/include/xen_driver_type.h
new file mode 100644
index 0000000000..585db28979
--- /dev/null
+++ b/tools/libxen/include/xen_driver_type.h
@@ -0,0 +1,77 @@
+/*
+ * 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_DRIVER_TYPE_H
+#define XEN_DRIVER_TYPE_H
+
+
+#include "xen_common.h"
+
+
+enum xen_driver_type
+{
+ /**
+ * use hardware emulation
+ */
+ XEN_DRIVER_TYPE_IOEMU,
+
+ /**
+ * use paravirtualised driver
+ */
+ XEN_DRIVER_TYPE_PARAVIRTUALISED
+};
+
+
+typedef struct xen_driver_type_set
+{
+ size_t size;
+ enum xen_driver_type contents[];
+} xen_driver_type_set;
+
+/**
+ * Allocate a xen_driver_type_set of the given size.
+ */
+extern xen_driver_type_set *
+xen_driver_type_set_alloc(size_t size);
+
+/**
+ * Free the given xen_driver_type_set. The given set must have been
+ * allocated by this library.
+ */
+extern void
+xen_driver_type_set_free(xen_driver_type_set *set);
+
+
+/**
+ * Return the name corresponding to the given code. This string must
+ * not be modified or freed.
+ */
+extern const char *
+xen_driver_type_to_string(enum xen_driver_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_driver_type
+xen_driver_type_from_string(xen_session *session, const char *str);
+
+
+#endif
diff --git a/tools/libxen/include/xen_driver_type_internal.h b/tools/libxen/include/xen_driver_type_internal.h
new file mode 100644
index 0000000000..c44639f692
--- /dev/null
+++ b/tools/libxen/include/xen_driver_type_internal.h
@@ -0,0 +1,37 @@
+/*
+ * 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
+ */
+
+
+/*
+ * Declarations of the abstract types used during demarshalling of enum
+ * xen_driver_type. Internal to this library -- do not use from outside.
+ */
+
+
+#ifndef XEN_DRIVER_TYPE_INTERNAL_H
+#define XEN_DRIVER_TYPE_INTERNAL_H
+
+
+#include "xen_internal.h"
+
+
+extern const abstract_type xen_driver_type_abstract_type_;
+extern const abstract_type xen_driver_type_set_abstract_type_;
+
+
+#endif
diff --git a/tools/libxen/include/xen_host.h b/tools/libxen/include/xen_host.h
new file mode 100644
index 0000000000..2264b603e4
--- /dev/null
+++ b/tools/libxen/include/xen_host.h
@@ -0,0 +1,292 @@
+/*
+ * 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_HOST_H
+#define XEN_HOST_H
+
+#include "xen_common.h"
+#include "xen_host_cpu_decl.h"
+#include "xen_host_decl.h"
+#include "xen_pif_decl.h"
+#include "xen_string_string_map.h"
+#include "xen_vm_decl.h"
+
+
+/*
+ * 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;
+ xen_string_string_map *software_version;
+ struct xen_vm_record_opt_set *resident_vms;
+ struct xen_pif_record_opt_set *pifs;
+ struct xen_host_cpu_record_opt_set *host_cpus;
+} 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 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 object with the specified UUID. !!!
+ */
+extern bool
+xen_host_get_by_uuid(xen_session *session, xen_host *result, char *uuid);
+
+
+/**
+ * Create a new host instance, and return its handle.
+ */
+extern bool
+xen_host_create(xen_session *session, xen_host *result, xen_host_record *record);
+
+
+/**
+ * Destroy the specified host instance.
+ */
+extern bool
+xen_host_destroy(xen_session *session, xen_host host);
+
+
+/**
+ * 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 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 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 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 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);
+
+
+/**
+ * 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);
+
+
+/**
+ * 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);
+
+
+/**
+ * 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_host_cpu.h b/tools/libxen/include/xen_host_cpu.h
new file mode 100644
index 0000000000..e0d4ee0c4b
--- /dev/null
+++ b/tools/libxen/include/xen_host_cpu.h
@@ -0,0 +1,239 @@
+/*
+ * 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_HOST_CPU_H
+#define XEN_HOST_CPU_H
+
+#include "xen_common.h"
+#include "xen_cpu_feature.h"
+#include "xen_host_cpu_decl.h"
+#include "xen_host_decl.h"
+
+
+/*
+ * 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;
+ struct xen_cpu_feature_set *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 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 object with the specified UUID. !!!
+ */
+extern bool
+xen_host_cpu_get_by_uuid(xen_session *session, xen_host_cpu *result, char *uuid);
+
+
+/**
+ * Create a new host_cpu instance, and return its handle.
+ */
+extern bool
+xen_host_cpu_create(xen_session *session, xen_host_cpu *result, xen_host_cpu_record *record);
+
+
+/**
+ * Destroy the specified host_cpu instance.
+ */
+extern bool
+xen_host_cpu_destroy(xen_session *session, xen_host_cpu host_cpu);
+
+
+/**
+ * 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 features field of the given host_cpu.
+ */
+extern bool
+xen_host_cpu_get_features(xen_session *session, struct xen_cpu_feature_set **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);
+
+
+#endif
diff --git a/tools/libxen/include/xen_host_cpu_decl.h b/tools/libxen/include/xen_host_cpu_decl.h
new file mode 100644
index 0000000000..1d24953ecc
--- /dev/null
+++ b/tools/libxen/include/xen_host_cpu_decl.h
@@ -0,0 +1,30 @@
+/*
+ * 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_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_host_decl.h b/tools/libxen/include/xen_host_decl.h
new file mode 100644
index 0000000000..affb676869
--- /dev/null
+++ b/tools/libxen/include/xen_host_decl.h
@@ -0,0 +1,30 @@
+/*
+ * 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_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_int_float_map.h b/tools/libxen/include/xen_int_float_map.h
new file mode 100644
index 0000000000..9cc4769d48
--- /dev/null
+++ b/tools/libxen/include/xen_int_float_map.h
@@ -0,0 +1,53 @@
+/*
+ * 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_INT_FLOAT_MAP_H
+#define XEN_INT_FLOAT_MAP_H
+
+
+#include "xen_common.h"
+
+
+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_internal.h b/tools/libxen/include/xen_internal.h
new file mode 100644
index 0000000000..043c4053f8
--- /dev/null
+++ b/tools/libxen/include/xen_internal.h
@@ -0,0 +1,193 @@
+/*
+ * 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_INTERNAL_H
+#define XEN_INTERNAL_H
+
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "xen_common.h"
+
+
+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;
+
+
+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__) \
+type__ ## _set * \
+type__ ## _set_alloc(size_t size) \
+{ \
+ return calloc(1, sizeof(type__ ## _set) + size * sizeof(type__)); \
+} \
+ \
+void \
+type__ ## _set_free(type__ ## _set *set) \
+{ \
+ if (set == NULL) \
+ { \
+ return; \
+ } \
+ size_t n = set->size; \
+ for (size_t i = 0; i < n; 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_network.h b/tools/libxen/include/xen_network.h
new file mode 100644
index 0000000000..a25babe492
--- /dev/null
+++ b/tools/libxen/include/xen_network.h
@@ -0,0 +1,273 @@
+/*
+ * 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_NETWORK_H
+#define XEN_NETWORK_H
+
+#include "xen_common.h"
+#include "xen_network_decl.h"
+#include "xen_pif_decl.h"
+#include "xen_vif_decl.h"
+
+
+/*
+ * 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;
+ char *default_gateway;
+ char *default_netmask;
+} 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 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 object 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 default_gateway field of the given network.
+ */
+extern bool
+xen_network_get_default_gateway(xen_session *session, char **result, xen_network network);
+
+
+/**
+ * Get the default_netmask field of the given network.
+ */
+extern bool
+xen_network_get_default_netmask(xen_session *session, char **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 default_gateway field of the given network.
+ */
+extern bool
+xen_network_set_default_gateway(xen_session *session, xen_network network, char *default_gateway);
+
+
+/**
+ * Set the default_netmask field of the given network.
+ */
+extern bool
+xen_network_set_default_netmask(xen_session *session, xen_network network, char *default_netmask);
+
+
+/**
+ * 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_network_decl.h b/tools/libxen/include/xen_network_decl.h
new file mode 100644
index 0000000000..d970c2af15
--- /dev/null
+++ b/tools/libxen/include/xen_network_decl.h
@@ -0,0 +1,30 @@
+/*
+ * 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_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_on_crash_behaviour.h b/tools/libxen/include/xen_on_crash_behaviour.h
new file mode 100644
index 0000000000..8286488659
--- /dev/null
+++ b/tools/libxen/include/xen_on_crash_behaviour.h
@@ -0,0 +1,97 @@
+/*
+ * 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_ON_CRASH_BEHAVIOUR_H
+#define XEN_ON_CRASH_BEHAVIOUR_H
+
+
+#include "xen_common.h"
+
+
+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_on_crash_behaviour_internal.h b/tools/libxen/include/xen_on_crash_behaviour_internal.h
new file mode 100644
index 0000000000..012398b814
--- /dev/null
+++ b/tools/libxen/include/xen_on_crash_behaviour_internal.h
@@ -0,0 +1,38 @@
+/*
+ * 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
+ */
+
+
+/*
+ * 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.h b/tools/libxen/include/xen_on_normal_exit.h
new file mode 100644
index 0000000000..3897fef24c
--- /dev/null
+++ b/tools/libxen/include/xen_on_normal_exit.h
@@ -0,0 +1,77 @@
+/*
+ * 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_ON_NORMAL_EXIT_H
+#define XEN_ON_NORMAL_EXIT_H
+
+
+#include "xen_common.h"
+
+
+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_on_normal_exit_internal.h b/tools/libxen/include/xen_on_normal_exit_internal.h
new file mode 100644
index 0000000000..3a94f8919c
--- /dev/null
+++ b/tools/libxen/include/xen_on_normal_exit_internal.h
@@ -0,0 +1,37 @@
+/*
+ * 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
+ */
+
+
+/*
+ * 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_pif.h b/tools/libxen/include/xen_pif.h
new file mode 100644
index 0000000000..143ba5709a
--- /dev/null
+++ b/tools/libxen/include/xen_pif.h
@@ -0,0 +1,290 @@
+/*
+ * 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_PIF_H
+#define XEN_PIF_H
+
+#include "xen_common.h"
+#include "xen_host_decl.h"
+#include "xen_network_decl.h"
+#include "xen_pif_decl.h"
+
+
+/*
+ * 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 *name;
+ struct xen_network_record_opt *network;
+ struct xen_host_record_opt *host;
+ char *mac;
+ int64_t mtu;
+ char *vlan;
+ double io_read_kbs;
+ double io_write_kbs;
+} 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 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 object with the specified UUID. !!!
+ */
+extern bool
+xen_pif_get_by_uuid(xen_session *session, xen_pif *result, char *uuid);
+
+
+/**
+ * Create a new PIF instance, and return its handle.
+ */
+extern bool
+xen_pif_create(xen_session *session, xen_pif *result, xen_pif_record *record);
+
+
+/**
+ * Destroy the specified PIF instance.
+ */
+extern bool
+xen_pif_destroy(xen_session *session, xen_pif pif);
+
+
+/**
+ * Get the uuid field of the given PIF.
+ */
+extern bool
+xen_pif_get_uuid(xen_session *session, char **result, xen_pif pif);
+
+
+/**
+ * Get the name field of the given PIF.
+ */
+extern bool
+xen_pif_get_name(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, char **result, xen_pif pif);
+
+
+/**
+ * Get the io/read_kbs field of the given PIF.
+ */
+extern bool
+xen_pif_get_io_read_kbs(xen_session *session, double *result, xen_pif pif);
+
+
+/**
+ * Get the io/write_kbs field of the given PIF.
+ */
+extern bool
+xen_pif_get_io_write_kbs(xen_session *session, double *result, xen_pif pif);
+
+
+/**
+ * Set the name field of the given PIF.
+ */
+extern bool
+xen_pif_set_name(xen_session *session, xen_pif pif, char *name);
+
+
+/**
+ * Set the network field of the given PIF.
+ */
+extern bool
+xen_pif_set_network(xen_session *session, xen_pif pif, xen_network network);
+
+
+/**
+ * Set the host field of the given PIF.
+ */
+extern bool
+xen_pif_set_host(xen_session *session, xen_pif pif, xen_host host);
+
+
+/**
+ * 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, char *vlan);
+
+
+#endif
diff --git a/tools/libxen/include/xen_pif_decl.h b/tools/libxen/include/xen_pif_decl.h
new file mode 100644
index 0000000000..b326bea3bc
--- /dev/null
+++ b/tools/libxen/include/xen_pif_decl.h
@@ -0,0 +1,30 @@
+/*
+ * 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_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_sr.h b/tools/libxen/include/xen_sr.h
new file mode 100644
index 0000000000..8359b5737b
--- /dev/null
+++ b/tools/libxen/include/xen_sr.h
@@ -0,0 +1,282 @@
+/*
+ * 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_SR_H
+#define XEN_SR_H
+
+#include "xen_common.h"
+#include "xen_sr_decl.h"
+#include "xen_vdi_decl.h"
+
+
+/*
+ * 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;
+ int64_t virtual_allocation;
+ int64_t physical_utilisation;
+ int64_t physical_size;
+ char *type;
+ char *location;
+} 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 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 object with the specified UUID. !!!
+ */
+extern bool
+xen_sr_get_by_uuid(xen_session *session, xen_sr *result, char *uuid);
+
+
+/**
+ * Create a new SR instance, and return its handle.
+ */
+extern bool
+xen_sr_create(xen_session *session, xen_sr *result, xen_sr_record *record);
+
+
+/**
+ * Destroy the specified SR instance.
+ */
+extern bool
+xen_sr_destroy(xen_session *session, xen_sr sr);
+
+
+/**
+ * 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 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 location field of the given SR.
+ */
+extern bool
+xen_sr_get_location(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);
+
+
+/**
+ * Take an exact copy of the Storage Repository; the cloned storage
+ * repository has the same type as its parent
+ */
+extern bool
+xen_sr_clone(xen_session *session, xen_sr *result, xen_sr sr, char *loc, char *name);
+
+
+/**
+ * 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_sr_decl.h b/tools/libxen/include/xen_sr_decl.h
new file mode 100644
index 0000000000..533e90c49e
--- /dev/null
+++ b/tools/libxen/include/xen_sr_decl.h
@@ -0,0 +1,30 @@
+/*
+ * 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_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_string_string_map.h b/tools/libxen/include/xen_string_string_map.h
new file mode 100644
index 0000000000..e3e5f6890b
--- /dev/null
+++ b/tools/libxen/include/xen_string_string_map.h
@@ -0,0 +1,53 @@
+/*
+ * 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_STRING_STRING_MAP_H
+#define XEN_STRING_STRING_MAP_H
+
+
+#include "xen_common.h"
+
+
+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_user.h b/tools/libxen/include/xen_user.h
new file mode 100644
index 0000000000..b426825585
--- /dev/null
+++ b/tools/libxen/include/xen_user.h
@@ -0,0 +1,204 @@
+/*
+ * 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_USER_H
+#define XEN_USER_H
+
+#include "xen_common.h"
+#include "xen_user_decl.h"
+
+
+/*
+ * 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 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 object 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_user_decl.h b/tools/libxen/include/xen_user_decl.h
new file mode 100644
index 0000000000..e5caae473b
--- /dev/null
+++ b/tools/libxen/include/xen_user_decl.h
@@ -0,0 +1,30 @@
+/*
+ * 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_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_vbd.h b/tools/libxen/include/xen_vbd.h
new file mode 100644
index 0000000000..2b9d8b05eb
--- /dev/null
+++ b/tools/libxen/include/xen_vbd.h
@@ -0,0 +1,285 @@
+/*
+ * 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_VBD_H
+#define XEN_VBD_H
+
+#include "xen_common.h"
+#include "xen_driver_type.h"
+#include "xen_vbd_decl.h"
+#include "xen_vbd_mode.h"
+#include "xen_vdi_decl.h"
+#include "xen_vm_decl.h"
+
+
+/*
+ * 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;
+ char *image;
+ enum xen_vbd_mode mode;
+ enum xen_driver_type driver;
+ double io_read_kbs;
+ double io_write_kbs;
+} 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 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 object 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 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 driver field of the given VBD.
+ */
+extern bool
+xen_vbd_get_driver(xen_session *session, enum xen_driver_type *result, xen_vbd vbd);
+
+
+/**
+ * Get the io/read_kbs field of the given VBD.
+ */
+extern bool
+xen_vbd_get_io_read_kbs(xen_session *session, double *result, xen_vbd vbd);
+
+
+/**
+ * Get the io/write_kbs field of the given VBD.
+ */
+extern bool
+xen_vbd_get_io_write_kbs(xen_session *session, double *result, xen_vbd vbd);
+
+
+/**
+ * Set the VM field of the given VBD.
+ */
+extern bool
+xen_vbd_set_vm(xen_session *session, xen_vbd vbd, xen_vm vm);
+
+
+/**
+ * Set the VDI field of the given VBD.
+ */
+extern bool
+xen_vbd_set_vdi(xen_session *session, xen_vbd vbd, xen_vdi vdi);
+
+
+/**
+ * Set the device field of the given VBD.
+ */
+extern bool
+xen_vbd_set_device(xen_session *session, xen_vbd vbd, char *device);
+
+
+/**
+ * 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 driver field of the given VBD.
+ */
+extern bool
+xen_vbd_set_driver(xen_session *session, xen_vbd vbd, enum xen_driver_type driver);
+
+
+/**
+ * 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);
+
+
+#endif
diff --git a/tools/libxen/include/xen_vbd_decl.h b/tools/libxen/include/xen_vbd_decl.h
new file mode 100644
index 0000000000..c6877866b6
--- /dev/null
+++ b/tools/libxen/include/xen_vbd_decl.h
@@ -0,0 +1,30 @@
+/*
+ * 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_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_vbd_mode.h b/tools/libxen/include/xen_vbd_mode.h
new file mode 100644
index 0000000000..b0cd1c2cf3
--- /dev/null
+++ b/tools/libxen/include/xen_vbd_mode.h
@@ -0,0 +1,77 @@
+/*
+ * 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_VBD_MODE_H
+#define XEN_VBD_MODE_H
+
+
+#include "xen_common.h"
+
+
+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_vbd_mode_internal.h b/tools/libxen/include/xen_vbd_mode_internal.h
new file mode 100644
index 0000000000..3efd4a63d5
--- /dev/null
+++ b/tools/libxen/include/xen_vbd_mode_internal.h
@@ -0,0 +1,37 @@
+/*
+ * 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
+ */
+
+
+/*
+ * 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_vdi.h b/tools/libxen/include/xen_vdi.h
new file mode 100644
index 0000000000..ba20f755c6
--- /dev/null
+++ b/tools/libxen/include/xen_vdi.h
@@ -0,0 +1,344 @@
+/*
+ * 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_VDI_H
+#define XEN_VDI_H
+
+#include "xen_common.h"
+#include "xen_sr_decl.h"
+#include "xen_vbd_decl.h"
+#include "xen_vdi_decl.h"
+#include "xen_vdi_type.h"
+
+
+/*
+ * 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;
+ int64_t virtual_size;
+ int64_t physical_utilisation;
+ int64_t sector_size;
+ enum xen_vdi_type type;
+ struct xen_vdi_record_opt *parent;
+ struct xen_vdi_record_opt_set *children;
+ bool sharable;
+ bool read_only;
+} 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 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 object 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 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 sector_size field of the given VDI.
+ */
+extern bool
+xen_vdi_get_sector_size(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 parent field of the given VDI.
+ */
+extern bool
+xen_vdi_get_parent(xen_session *session, xen_vdi *result, xen_vdi vdi);
+
+
+/**
+ * Get the children field of the given VDI.
+ */
+extern bool
+xen_vdi_get_children(xen_session *session, struct xen_vdi_set **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);
+
+
+/**
+ * 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 SR field of the given VDI.
+ */
+extern bool
+xen_vdi_set_sr(xen_session *session, xen_vdi vdi, xen_sr sr);
+
+
+/**
+ * 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);
+
+
+/**
+ * Take an exact copy of the VDI; the snapshot lives in the same
+ * Storage Repository as its parent.
+ */
+extern bool
+xen_vdi_snapshot(xen_session *session, xen_vdi *result, xen_vdi vdi);
+
+
+/**
+ * Resize the vdi to the size.
+ */
+extern bool
+xen_vdi_resize(xen_session *session, xen_vdi vdi, int64_t size);
+
+
+#endif
diff --git a/tools/libxen/include/xen_vdi_decl.h b/tools/libxen/include/xen_vdi_decl.h
new file mode 100644
index 0000000000..34692a2495
--- /dev/null
+++ b/tools/libxen/include/xen_vdi_decl.h
@@ -0,0 +1,30 @@
+/*
+ * 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_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_vdi_type.h b/tools/libxen/include/xen_vdi_type.h
new file mode 100644
index 0000000000..33ba7c61bf
--- /dev/null
+++ b/tools/libxen/include/xen_vdi_type.h
@@ -0,0 +1,82 @@
+/*
+ * 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_VDI_TYPE_H
+#define XEN_VDI_TYPE_H
+
+
+#include "xen_common.h"
+
+
+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
+};
+
+
+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_vdi_type_internal.h b/tools/libxen/include/xen_vdi_type_internal.h
new file mode 100644
index 0000000000..1de23c8c83
--- /dev/null
+++ b/tools/libxen/include/xen_vdi_type_internal.h
@@ -0,0 +1,37 @@
+/*
+ * 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
+ */
+
+
+/*
+ * 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_vif.h b/tools/libxen/include/xen_vif.h
new file mode 100644
index 0000000000..8930a7849f
--- /dev/null
+++ b/tools/libxen/include/xen_vif.h
@@ -0,0 +1,305 @@
+/*
+ * 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_VIF_H
+#define XEN_VIF_H
+
+#include "xen_common.h"
+#include "xen_driver_type.h"
+#include "xen_network_decl.h"
+#include "xen_vif_decl.h"
+#include "xen_vm_decl.h"
+
+
+/*
+ * 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 *name;
+ enum xen_driver_type type;
+ char *device;
+ struct xen_network_record_opt *network;
+ struct xen_vm_record_opt *vm;
+ char *mac;
+ int64_t mtu;
+ double io_read_kbs;
+ double io_write_kbs;
+} 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 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 object 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 name field of the given VIF.
+ */
+extern bool
+xen_vif_get_name(xen_session *session, char **result, xen_vif vif);
+
+
+/**
+ * Get the type field of the given VIF.
+ */
+extern bool
+xen_vif_get_type(xen_session *session, enum xen_driver_type *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 io/read_kbs field of the given VIF.
+ */
+extern bool
+xen_vif_get_io_read_kbs(xen_session *session, double *result, xen_vif vif);
+
+
+/**
+ * Get the io/write_kbs field of the given VIF.
+ */
+extern bool
+xen_vif_get_io_write_kbs(xen_session *session, double *result, xen_vif vif);
+
+
+/**
+ * Set the name field of the given VIF.
+ */
+extern bool
+xen_vif_set_name(xen_session *session, xen_vif vif, char *name);
+
+
+/**
+ * Set the type field of the given VIF.
+ */
+extern bool
+xen_vif_set_type(xen_session *session, xen_vif vif, enum xen_driver_type type);
+
+
+/**
+ * Set the device field of the given VIF.
+ */
+extern bool
+xen_vif_set_device(xen_session *session, xen_vif vif, char *device);
+
+
+/**
+ * Set the network field of the given VIF.
+ */
+extern bool
+xen_vif_set_network(xen_session *session, xen_vif vif, xen_network network);
+
+
+/**
+ * Set the VM field of the given VIF.
+ */
+extern bool
+xen_vif_set_vm(xen_session *session, xen_vif vif, xen_vm vm);
+
+
+/**
+ * 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);
+
+
+#endif
diff --git a/tools/libxen/include/xen_vif_decl.h b/tools/libxen/include/xen_vif_decl.h
new file mode 100644
index 0000000000..6a130f7154
--- /dev/null
+++ b/tools/libxen/include/xen_vif_decl.h
@@ -0,0 +1,30 @@
+/*
+ * 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_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_vm.h b/tools/libxen/include/xen_vm.h
new file mode 100644
index 0000000000..f589a3eea5
--- /dev/null
+++ b/tools/libxen/include/xen_vm.h
@@ -0,0 +1,819 @@
+/*
+ * 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_VM_H
+#define XEN_VM_H
+
+#include "xen_boot_type.h"
+#include "xen_common.h"
+#include "xen_cpu_feature.h"
+#include "xen_host_decl.h"
+#include "xen_int_float_map.h"
+#include "xen_on_crash_behaviour.h"
+#include "xen_on_normal_exit.h"
+#include "xen_string_string_map.h"
+#include "xen_vbd_decl.h"
+#include "xen_vif_decl.h"
+#include "xen_vm_decl.h"
+#include "xen_vm_power_state.h"
+#include "xen_vtpm_decl.h"
+
+
+/*
+ * 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;
+ struct xen_host_record_opt *resident_on;
+ int64_t memory_static_max;
+ int64_t memory_dynamic_max;
+ int64_t memory_actual;
+ int64_t memory_dynamic_min;
+ int64_t memory_static_min;
+ char *vcpus_policy;
+ char *vcpus_params;
+ int64_t vcpus_number;
+ xen_int_float_map *vcpus_utilisation;
+ struct xen_cpu_feature_set *vcpus_features_required;
+ struct xen_cpu_feature_set *vcpus_features_can_use;
+ struct xen_cpu_feature_set *vcpus_features_force_on;
+ struct xen_cpu_feature_set *vcpus_features_force_off;
+ enum xen_on_normal_exit actions_after_shutdown;
+ enum xen_on_normal_exit actions_after_reboot;
+ enum xen_on_normal_exit actions_after_suspend;
+ enum xen_on_crash_behaviour actions_after_crash;
+ struct xen_vif_record_opt_set *vifs;
+ struct xen_vbd_record_opt_set *vbds;
+ struct xen_vtpm_record_opt_set *vtpms;
+ char *bios_boot;
+ bool platform_std_vga;
+ char *platform_serial;
+ bool platform_localtime;
+ bool platform_clock_offset;
+ bool platform_enable_audio;
+ char *builder;
+ enum xen_boot_type boot_method;
+ char *kernel_kernel;
+ char *kernel_initrd;
+ char *kernel_args;
+ char *grub_cmdline;
+ char *pci_bus;
+ xen_string_string_map *tools_version;
+ xen_string_string_map *otherconfig;
+} 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 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 object 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 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/actual field of the given VM.
+ */
+extern bool
+xen_vm_get_memory_actual(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/policy field of the given VM.
+ */
+extern bool
+xen_vm_get_vcpus_policy(xen_session *session, char **result, xen_vm vm);
+
+
+/**
+ * Get the VCPUs/params field of the given VM.
+ */
+extern bool
+xen_vm_get_vcpus_params(xen_session *session, char **result, xen_vm vm);
+
+
+/**
+ * Get the VCPUs/number field of the given VM.
+ */
+extern bool
+xen_vm_get_vcpus_number(xen_session *session, int64_t *result, xen_vm vm);
+
+
+/**
+ * Get the VCPUs/utilisation field of the given VM.
+ */
+extern bool
+xen_vm_get_vcpus_utilisation(xen_session *session, xen_int_float_map **result, xen_vm vm);
+
+
+/**
+ * Get the VCPUs/features/required field of the given VM.
+ */
+extern bool
+xen_vm_get_vcpus_features_required(xen_session *session, struct xen_cpu_feature_set **result, xen_vm vm);
+
+
+/**
+ * Get the VCPUs/features/can_use field of the given VM.
+ */
+extern bool
+xen_vm_get_vcpus_features_can_use(xen_session *session, struct xen_cpu_feature_set **result, xen_vm vm);
+
+
+/**
+ * Get the VCPUs/features/force_on field of the given VM.
+ */
+extern bool
+xen_vm_get_vcpus_features_force_on(xen_session *session, struct xen_cpu_feature_set **result, xen_vm vm);
+
+
+/**
+ * Get the VCPUs/features/force_off field of the given VM.
+ */
+extern bool
+xen_vm_get_vcpus_features_force_off(xen_session *session, struct xen_cpu_feature_set **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_suspend field of the given VM.
+ */
+extern bool
+xen_vm_get_actions_after_suspend(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 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 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 bios/boot field of the given VM.
+ */
+extern bool
+xen_vm_get_bios_boot(xen_session *session, char **result, xen_vm vm);
+
+
+/**
+ * Get the platform/std_VGA field of the given VM.
+ */
+extern bool
+xen_vm_get_platform_std_vga(xen_session *session, bool *result, xen_vm vm);
+
+
+/**
+ * Get the platform/serial field of the given VM.
+ */
+extern bool
+xen_vm_get_platform_serial(xen_session *session, char **result, xen_vm vm);
+
+
+/**
+ * Get the platform/localtime field of the given VM.
+ */
+extern bool
+xen_vm_get_platform_localtime(xen_session *session, bool *result, xen_vm vm);
+
+
+/**
+ * Get the platform/clock_offset field of the given VM.
+ */
+extern bool
+xen_vm_get_platform_clock_offset(xen_session *session, bool *result, xen_vm vm);
+
+
+/**
+ * Get the platform/enable_audio field of the given VM.
+ */
+extern bool
+xen_vm_get_platform_enable_audio(xen_session *session, bool *result, xen_vm vm);
+
+
+/**
+ * Get the builder field of the given VM.
+ */
+extern bool
+xen_vm_get_builder(xen_session *session, char **result, xen_vm vm);
+
+
+/**
+ * Get the boot_method field of the given VM.
+ */
+extern bool
+xen_vm_get_boot_method(xen_session *session, enum xen_boot_type *result, xen_vm vm);
+
+
+/**
+ * Get the kernel/kernel field of the given VM.
+ */
+extern bool
+xen_vm_get_kernel_kernel(xen_session *session, char **result, xen_vm vm);
+
+
+/**
+ * Get the kernel/initrd field of the given VM.
+ */
+extern bool
+xen_vm_get_kernel_initrd(xen_session *session, char **result, xen_vm vm);
+
+
+/**
+ * Get the kernel/args field of the given VM.
+ */
+extern bool
+xen_vm_get_kernel_args(xen_session *session, char **result, xen_vm vm);
+
+
+/**
+ * Get the grub/cmdline field of the given VM.
+ */
+extern bool
+xen_vm_get_grub_cmdline(xen_session *session, char **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 tools_version field of the given VM.
+ */
+extern bool
+xen_vm_get_tools_version(xen_session *session, xen_string_string_map **result, xen_vm vm);
+
+
+/**
+ * Get the otherConfig field of the given VM.
+ */
+extern bool
+xen_vm_get_otherconfig(xen_session *session, xen_string_string_map **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 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 VCPUs/policy field of the given VM.
+ */
+extern bool
+xen_vm_set_vcpus_policy(xen_session *session, xen_vm vm, char *policy);
+
+
+/**
+ * Set the VCPUs/params field of the given VM.
+ */
+extern bool
+xen_vm_set_vcpus_params(xen_session *session, xen_vm vm, char *params);
+
+
+/**
+ * Set the VCPUs/features/force_on field of the given VM.
+ */
+extern bool
+xen_vm_set_vcpus_features_force_on(xen_session *session, xen_vm vm, struct xen_cpu_feature_set *force_on);
+
+
+/**
+ * Set the VCPUs/features/force_off field of the given VM.
+ */
+extern bool
+xen_vm_set_vcpus_features_force_off(xen_session *session, xen_vm vm, struct xen_cpu_feature_set *force_off);
+
+
+/**
+ * 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_suspend field of the given VM.
+ */
+extern bool
+xen_vm_set_actions_after_suspend(xen_session *session, xen_vm vm, enum xen_on_normal_exit after_suspend);
+
+
+/**
+ * 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 bios/boot field of the given VM.
+ */
+extern bool
+xen_vm_set_bios_boot(xen_session *session, xen_vm vm, char *boot);
+
+
+/**
+ * Set the platform/std_VGA field of the given VM.
+ */
+extern bool
+xen_vm_set_platform_std_vga(xen_session *session, xen_vm vm, bool std_vga);
+
+
+/**
+ * Set the platform/serial field of the given VM.
+ */
+extern bool
+xen_vm_set_platform_serial(xen_session *session, xen_vm vm, char *serial);
+
+
+/**
+ * Set the platform/localtime field of the given VM.
+ */
+extern bool
+xen_vm_set_platform_localtime(xen_session *session, xen_vm vm, bool localtime);
+
+
+/**
+ * Set the platform/clock_offset field of the given VM.
+ */
+extern bool
+xen_vm_set_platform_clock_offset(xen_session *session, xen_vm vm, bool clock_offset);
+
+
+/**
+ * Set the platform/enable_audio field of the given VM.
+ */
+extern bool
+xen_vm_set_platform_enable_audio(xen_session *session, xen_vm vm, bool enable_audio);
+
+
+/**
+ * Set the builder field of the given VM.
+ */
+extern bool
+xen_vm_set_builder(xen_session *session, xen_vm vm, char *builder);
+
+
+/**
+ * Set the boot_method field of the given VM.
+ */
+extern bool
+xen_vm_set_boot_method(xen_session *session, xen_vm vm, enum xen_boot_type boot_method);
+
+
+/**
+ * Set the kernel/kernel field of the given VM.
+ */
+extern bool
+xen_vm_set_kernel_kernel(xen_session *session, xen_vm vm, char *kernel);
+
+
+/**
+ * Set the kernel/initrd field of the given VM.
+ */
+extern bool
+xen_vm_set_kernel_initrd(xen_session *session, xen_vm vm, char *initrd);
+
+
+/**
+ * Set the kernel/args field of the given VM.
+ */
+extern bool
+xen_vm_set_kernel_args(xen_session *session, xen_vm vm, char *args);
+
+
+/**
+ * Set the grub/cmdline field of the given VM.
+ */
+extern bool
+xen_vm_set_grub_cmdline(xen_session *session, xen_vm vm, char *cmdline);
+
+
+/**
+ * Set the otherConfig field of the given VM.
+ */
+extern bool
+xen_vm_set_otherconfig(xen_session *session, xen_vm vm, xen_string_string_map *otherconfig);
+
+
+/**
+ * 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.
+ */
+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.
+ */
+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.
+ */
+extern bool
+xen_vm_suspend(xen_session *session, xen_vm vm);
+
+
+/**
+ * Awaken the specified VM and resume it.
+ */
+extern bool
+xen_vm_resume(xen_session *session, xen_vm vm, bool start_paused);
+
+
+/**
+ * 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);
+
+
+/**
+ * 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);
+
+
+#endif
diff --git a/tools/libxen/include/xen_vm_decl.h b/tools/libxen/include/xen_vm_decl.h
new file mode 100644
index 0000000000..815b036ea5
--- /dev/null
+++ b/tools/libxen/include/xen_vm_decl.h
@@ -0,0 +1,30 @@
+/*
+ * 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_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_vm_power_state.h b/tools/libxen/include/xen_vm_power_state.h
new file mode 100644
index 0000000000..3e805ed671
--- /dev/null
+++ b/tools/libxen/include/xen_vm_power_state.h
@@ -0,0 +1,97 @@
+/*
+ * 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_VM_POWER_STATE_H
+#define XEN_VM_POWER_STATE_H
+
+
+#include "xen_common.h"
+
+
+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,
+
+ /**
+ * Shutting Down
+ */
+ XEN_VM_POWER_STATE_SHUTTINGDOWN,
+
+ /**
+ * 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_vm_power_state_internal.h b/tools/libxen/include/xen_vm_power_state_internal.h
new file mode 100644
index 0000000000..2c88856c08
--- /dev/null
+++ b/tools/libxen/include/xen_vm_power_state_internal.h
@@ -0,0 +1,37 @@
+/*
+ * 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
+ */
+
+
+/*
+ * 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/include/xen_vtpm.h b/tools/libxen/include/xen_vtpm.h
new file mode 100644
index 0000000000..f55d74217b
--- /dev/null
+++ b/tools/libxen/include/xen_vtpm.h
@@ -0,0 +1,216 @@
+/*
+ * 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_H
+#define XEN_VTPM_H
+
+#include "xen_common.h"
+#include "xen_driver_type.h"
+#include "xen_vm_decl.h"
+#include "xen_vtpm_decl.h"
+
+
+/*
+ * 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;
+ enum xen_driver_type driver;
+ int64_t instance;
+} 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 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 object 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 driver field of the given VTPM.
+ */
+extern bool
+xen_vtpm_get_driver(xen_session *session, enum xen_driver_type *result, xen_vtpm vtpm);
+
+
+/**
+ * Get the instance field of the given VTPM.
+ */
+extern bool
+xen_vtpm_get_instance(xen_session *session, int64_t *result, xen_vtpm vtpm);
+
+
+#endif
diff --git a/tools/libxen/include/xen_vtpm_decl.h b/tools/libxen/include/xen_vtpm_decl.h
new file mode 100644
index 0000000000..7798e3856d
--- /dev/null
+++ b/tools/libxen/include/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/src/xen_boot_type.c b/tools/libxen/src/xen_boot_type.c
new file mode 100644
index 0000000000..798d09ac2b
--- /dev/null
+++ b/tools/libxen/src/xen_boot_type.c
@@ -0,0 +1,83 @@
+/*
+ * 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
+ */
+
+#include <string.h>
+
+#include "xen_internal.h"
+#include "xen_boot_type.h"
+#include "xen_boot_type_internal.h"
+
+
+/*
+ * Maintain this in the same order as the enum declaration!
+ */
+static const char *lookup_table[] =
+{
+ "bios",
+ "grub",
+ "kernel_external",
+ "kernel_internal"
+};
+
+
+extern xen_boot_type_set *
+xen_boot_type_set_alloc(size_t size)
+{
+ return calloc(1, sizeof(xen_boot_type_set) +
+ size * sizeof(enum xen_boot_type));
+}
+
+
+extern void
+xen_boot_type_set_free(xen_boot_type_set *set)
+{
+ free(set);
+}
+
+
+const char *
+xen_boot_type_to_string(enum xen_boot_type val)
+{
+ return lookup_table[val];
+}
+
+
+extern enum xen_boot_type
+xen_boot_type_from_string(xen_session *session, const char *str)
+{
+ return ENUM_LOOKUP(session, str, lookup_table);
+}
+
+
+const abstract_type xen_boot_type_abstract_type_ =
+ {
+ .typename = ENUM,
+ .enum_marshaller =
+ (const char *(*)(int))&xen_boot_type_to_string,
+ .enum_demarshaller =
+ (int (*)(xen_session *, const char *))&xen_boot_type_from_string
+ };
+
+
+const abstract_type xen_boot_type_set_abstract_type_ =
+ {
+ .typename = SET,
+ .child = &xen_boot_type_abstract_type_
+ };
+
+
diff --git a/tools/libxen/src/xen_common.c b/tools/libxen/src/xen_common.c
new file mode 100644
index 0000000000..bd370bb257
--- /dev/null
+++ b/tools/libxen/src/xen_common.c
@@ -0,0 +1,1363 @@
+/*
+ * 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
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xmlsave.h>
+#include <libxml/xmlstring.h>
+#include <libxml/xpath.h>
+
+#include "xen_common.h"
+#include "xen_internal.h"
+#include "xen_int_float_map.h"
+#include "xen_string_string_map.h"
+
+
+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 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 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 *);
+
+
+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;
+}
+
+
+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);
+}
+
+
+int
+xen_session_get_this_host(xen_session *session, xen_host *result)
+{
+ abstract_value params[] =
+ {
+ };
+
+ xen_call_(session, "session.get_this_host", params, 0,
+ &abstract_type_string, result);
+ 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)
+{
+ if (!s->ok)
+ {
+ return;
+ }
+
+ abstract_value *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)
+{
+ if (!session->ok)
+ {
+ /* Don't wipe out the earlier error message with this one. */
+ return;
+ }
+
+ char **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)
+{
+ if (!session->ok)
+ {
+ /* Don't wipe out the earlier error message with this one. */
+ return;
+ }
+
+ char **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_container_node(xmlNode *n, char *type)
+{
+ return
+ n->type == XML_ELEMENT_NODE &&
+ 0 == strcmp((char *)n->name, 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)
+{
+ return
+ is_container_node(n, "value") &&
+ 0 == strcmp((char *)n->children->name, type) ?
+ (n->children->children == NULL ?
+ xmlStrdup(BAD_CAST("")) :
+ xmlNodeGetContent(n->children->children)) :
+ 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 : 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)
+ {
+ server_error(
+ s, "Expected an Enum from the server, but didn't get one");
+ }
+ 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)
+ {
+ server_error(
+ s, "Expected a Float from the server, but didn't get one");
+ }
+ else
+ {
+ ((double *)value)[slot] = atof((char *)string);
+ free(string);
+ }
+ }
+ break;
+
+ case BOOL:
+ {
+ xmlChar *string = string_from_value(value_node, "boolean");
+ if (string == NULL)
+ {
+ server_error(
+ s, "Expected a Bool from the server, but didn't get one");
+ }
+ else
+ {
+ ((bool *)value)[slot] = (0 == strcmp((char *)string, "1"));
+ 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
+ {
+ xmlNode *data_node = value_node->children->children;
+ int n = count_children(data_node, "value");
+
+ const abstract_type *member_type = result_type->child;
+ size_t member_size = size_of_member(member_type);
+
+ arbitrary_set *set =
+ calloc(1, sizeof(arbitrary_set) + member_size * n);
+ set->size = n;
+ int i = 0;
+ xmlNode *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") ||
+ 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;
+ int 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;
+
+ arbitrary_map *map =
+ calloc(1, sizeof(arbitrary_map) + struct_size * n);
+ map->size = n;
+ int i = 0;
+ xmlNode *cur = struct_node->children;
+
+ while (cur != NULL)
+ {
+ if (0 == strcmp((char *)cur->name, "member"))
+ {
+ if (cur->children == NULL || cur->last == cur->children)
+ {
+ server_error(s, "Malformed Map");
+ free(map);
+ return;
+ }
+
+ xmlChar *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"))
+ {
+ if (cur->children == NULL || cur->last == cur->children)
+ {
+ server_error(s, "Malformed Struct");
+ free(result);
+ free(checklist);
+ return;
+ }
+
+ xmlChar *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)
+ {
+ server_error_2(s,
+ "Struct did not contain expected field",
+ mem->key);
+ free(result);
+ free(checklist);
+ return;
+ }
+ }
+
+ 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 *);
+
+ 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)
+{
+ 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;
+ }
+
+ xmlNode *fault_node0 = xpathObj->nodesetval->nodeTab[0];
+ xmlNode *fault_node1 = xpathObj->nodesetval->nodeTab[1];
+
+ xmlChar *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;
+ }
+
+ xmlChar *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;
+ }
+
+ char **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)
+ {
+ session->ok = false;
+
+ char **c = (char **)error_descriptions->contents;
+ int n = error_descriptions->size;
+
+ char **strings = malloc(3 * sizeof(char *));
+ for (int i = 0; i < n; i++)
+ {
+ strings[i] = xen_strdup_(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);
+
+ if (doc == NULL)
+ {
+ server_error(session, "Couldn't parse the server response");
+ return;
+ }
+
+ xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc);
+ if (xpathCtx == NULL)
+ {
+ xmlFreeDoc(doc);
+ server_error(session, "Couldn't create XPath context");
+ return;
+ }
+
+ xmlXPathObjectPtr 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;
+ }
+
+ xmlNode *node0 = xpathObj->nodesetval->nodeTab[0];
+ xmlNode *node1 = xpathObj->nodesetval->nodeTab[1];
+
+ xmlChar *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 char *
+make_body(const char *method_name, abstract_value params[], int param_count)
+{
+ char buf[20];
+
+ xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
+ xmlNode *methodCall = xmlNewNode(NULL, BAD_CAST "methodCall");
+ xmlDocSetRootElement(doc, methodCall);
+
+ xmlNewChild(methodCall, NULL, BAD_CAST "methodName",
+ BAD_CAST method_name);
+
+ xmlNode *params_node =
+ xmlNewChild(methodCall, NULL, BAD_CAST "params", NULL);
+
+ for (int p = 0; p < param_count; p++)
+ {
+ abstract_value *v = params + p;
+ switch (v->type->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 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;
+
+ default:
+ assert(false);
+ }
+ }
+
+ xmlBufferPtr buffer = xmlBufferCreate();
+ xmlSaveCtxtPtr save_ctxt =
+ xmlSaveToBuffer(buffer, NULL, XML_SAVE_NO_XHTML);
+
+ if (xmlSaveDoc(save_ctxt, doc) == -1)
+ {
+ return NULL;
+ }
+
+ xmlFreeDoc(doc);
+ xmlSaveClose(save_ctxt);
+ xmlChar *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:
+ {
+ arbitrary_record_opt *val = *(arbitrary_record_opt **)value;
+ if (val != NULL)
+ {
+ if (val->is_record)
+ {
+ adder(node, key, "string", val->u.record->handle);
+ }
+ else
+ {
+ adder(node, key, "string", val->u.handle);
+ }
+ }
+ }
+ break;
+
+ case STRING:
+ {
+ char *val = *(char **)value;
+ if (val != NULL)
+ {
+ adder(node, key, "string", val);
+ }
+ }
+ break;
+
+ case INT:
+ {
+ int64_t val = *(int64_t *)value;
+ snprintf(buf, sizeof(buf), "%"PRId64, val);
+ adder(node, key, "string", buf);
+ }
+ 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 ENUM:
+ {
+ int val = *(int *)value;
+ adder(node, key, "string", type->enum_marshaller(val));
+ }
+ 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 = set_val->contents + (i * member_size);
+ add_struct_value(member_type, member_value,
+ add_unnamed_value, NULL, data_node);
+ }
+ }
+ }
+ break;
+
+ case STRUCT:
+ case MAP:
+ {
+ /* 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. We don't need maps either. */
+ }
+ 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");
+
+ xmlNewChild(member_node, NULL, BAD_CAST "name", BAD_CAST name);
+
+ xmlNode *value_node = add_container(member_node, "value");
+ xmlNode *array_node = add_container(value_node, "array");
+
+ return add_container(array_node, "data");
+
+}
+
+
+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
+ };
diff --git a/tools/libxen/src/xen_cpu_feature.c b/tools/libxen/src/xen_cpu_feature.c
new file mode 100644
index 0000000000..98fb64f9e3
--- /dev/null
+++ b/tools/libxen/src/xen_cpu_feature.c
@@ -0,0 +1,143 @@
+/*
+ * 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
+ */
+
+#include <string.h>
+
+#include "xen_internal.h"
+#include "xen_cpu_feature.h"
+#include "xen_cpu_feature_internal.h"
+
+
+/*
+ * Maintain this in the same order as the enum declaration!
+ */
+static const char *lookup_table[] =
+{
+ "FPU",
+ "VME",
+ "DE",
+ "PSE",
+ "TSC",
+ "MSR",
+ "PAE",
+ "MCE",
+ "CX8",
+ "APIC",
+ "SEP",
+ "MTRR",
+ "PGE",
+ "MCA",
+ "CMOV",
+ "PAT",
+ "PSE36",
+ "PN",
+ "CLFLSH",
+ "DTES",
+ "ACPI",
+ "MMX",
+ "FXSR",
+ "XMM",
+ "XMM2",
+ "SELFSNOOP",
+ "HT",
+ "ACC",
+ "IA64",
+ "SYSCALL",
+ "MP",
+ "NX",
+ "MMXEXT",
+ "LM",
+ "3DNOWEXT",
+ "3DNOW",
+ "RECOVERY",
+ "LONGRUN",
+ "LRTI",
+ "CXMMX",
+ "K6_MTRR",
+ "CYRIX_ARR",
+ "CENTAUR_MCR",
+ "K8",
+ "K7",
+ "P3",
+ "P4",
+ "CONSTANT_TSC",
+ "FXSAVE_LEAK",
+ "XMM3",
+ "MWAIT",
+ "DSCPL",
+ "EST",
+ "TM2",
+ "CID",
+ "CX16",
+ "XTPR",
+ "XSTORE",
+ "XSTORE_EN",
+ "XCRYPT",
+ "XCRYPT_EN",
+ "LAHF_LM",
+ "CMP_LEGACY",
+ "VMX"
+};
+
+
+extern xen_cpu_feature_set *
+xen_cpu_feature_set_alloc(size_t size)
+{
+ return calloc(1, sizeof(xen_cpu_feature_set) +
+ size * sizeof(enum xen_cpu_feature));
+}
+
+
+extern void
+xen_cpu_feature_set_free(xen_cpu_feature_set *set)
+{
+ free(set);
+}
+
+
+const char *
+xen_cpu_feature_to_string(enum xen_cpu_feature val)
+{
+ return lookup_table[val];
+}
+
+
+extern enum xen_cpu_feature
+xen_cpu_feature_from_string(xen_session *session, const char *str)
+{
+ return ENUM_LOOKUP(session, str, lookup_table);
+}
+
+
+const abstract_type xen_cpu_feature_abstract_type_ =
+ {
+ .typename = ENUM,
+ .enum_marshaller =
+ (const char *(*)(int))&xen_cpu_feature_to_string,
+ .enum_demarshaller =
+ (int (*)(xen_session *, const char *))&xen_cpu_feature_from_string
+ };
+
+
+const abstract_type xen_cpu_feature_set_abstract_type_ =
+ {
+ .typename = SET,
+ .child = &xen_cpu_feature_abstract_type_
+ };
+
+
diff --git a/tools/libxen/src/xen_driver_type.c b/tools/libxen/src/xen_driver_type.c
new file mode 100644
index 0000000000..fe95d84f0c
--- /dev/null
+++ b/tools/libxen/src/xen_driver_type.c
@@ -0,0 +1,81 @@
+/*
+ * 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
+ */
+
+#include <string.h>
+
+#include "xen_internal.h"
+#include "xen_driver_type.h"
+#include "xen_driver_type_internal.h"
+
+
+/*
+ * Maintain this in the same order as the enum declaration!
+ */
+static const char *lookup_table[] =
+{
+ "ioemu",
+ "paravirtualised"
+};
+
+
+extern xen_driver_type_set *
+xen_driver_type_set_alloc(size_t size)
+{
+ return calloc(1, sizeof(xen_driver_type_set) +
+ size * sizeof(enum xen_driver_type));
+}
+
+
+extern void
+xen_driver_type_set_free(xen_driver_type_set *set)
+{
+ free(set);
+}
+
+
+const char *
+xen_driver_type_to_string(enum xen_driver_type val)
+{
+ return lookup_table[val];
+}
+
+
+extern enum xen_driver_type
+xen_driver_type_from_string(xen_session *session, const char *str)
+{
+ return ENUM_LOOKUP(session, str, lookup_table);
+}
+
+
+const abstract_type xen_driver_type_abstract_type_ =
+ {
+ .typename = ENUM,
+ .enum_marshaller =
+ (const char *(*)(int))&xen_driver_type_to_string,
+ .enum_demarshaller =
+ (int (*)(xen_session *, const char *))&xen_driver_type_from_string
+ };
+
+
+const abstract_type xen_driver_type_set_abstract_type_ =
+ {
+ .typename = SET,
+ .child = &xen_driver_type_abstract_type_
+ };
+
+
diff --git a/tools/libxen/src/xen_host.c b/tools/libxen/src/xen_host.c
new file mode 100644
index 0000000000..931a0c44ce
--- /dev/null
+++ b/tools/libxen/src/xen_host.c
@@ -0,0 +1,390 @@
+/*
+ * 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
+ */
+
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "xen_common.h"
+#include "xen_host.h"
+#include "xen_host_cpu.h"
+#include "xen_internal.h"
+#include "xen_pif.h"
+#include "xen_string_string_map.h"
+#include "xen_vm.h"
+
+
+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 = "software_version",
+ .type = &abstract_type_string_string_map,
+ .offset = offsetof(xen_host_record, software_version) },
+ { .key = "resident_VMs",
+ .type = &abstract_type_ref_set,
+ .offset = offsetof(xen_host_record, resident_vms) },
+ { .key = "PIFs",
+ .type = &abstract_type_ref_set,
+ .offset = offsetof(xen_host_record, pifs) },
+ { .key = "host_CPUs",
+ .type = &abstract_type_ref_set,
+ .offset = offsetof(xen_host_record, host_cpus) }
+ };
+
+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);
+ xen_string_string_map_free(record->software_version);
+ xen_vm_record_opt_set_free(record->resident_vms);
+ xen_pif_record_opt_set_free(record->pifs);
+ xen_host_cpu_record_opt_set_free(record->host_cpus);
+ 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_create(xen_session *session, xen_host *result, xen_host_record *record)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &xen_host_record_abstract_type_,
+ .u.struct_val = record }
+ };
+
+ abstract_type result_type = abstract_type_string;
+
+ *result = NULL;
+ XEN_CALL_("host.create");
+ return session->ok;
+}
+
+
+bool
+xen_host_destroy(xen_session *session, xen_host host)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = host }
+ };
+
+ xen_call_(session, "host.destroy", param_values, 1, NULL, NULL);
+ 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_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_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_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_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_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_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_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)
+{
+ *result = session->ok ? xen_strdup_((char *)host) : NULL;
+ 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 0000000000..ff73dcd9b9
--- /dev/null
+++ b/tools/libxen/src/xen_host_cpu.c
@@ -0,0 +1,287 @@
+/*
+ * 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
+ */
+
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "xen_common.h"
+#include "xen_cpu_feature.h"
+#include "xen_cpu_feature_internal.h"
+#include "xen_host.h"
+#include "xen_host_cpu.h"
+#include "xen_internal.h"
+
+
+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 = "features",
+ .type = &xen_cpu_feature_set_abstract_type_,
+ .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);
+ xen_cpu_feature_set_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_create(xen_session *session, xen_host_cpu *result, xen_host_cpu_record *record)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &xen_host_cpu_record_abstract_type_,
+ .u.struct_val = record }
+ };
+
+ abstract_type result_type = abstract_type_string;
+
+ *result = NULL;
+ XEN_CALL_("host_cpu.create");
+ return session->ok;
+}
+
+
+bool
+xen_host_cpu_destroy(xen_session *session, xen_host_cpu host_cpu)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = host_cpu }
+ };
+
+ xen_call_(session, "host_cpu.destroy", param_values, 1, NULL, NULL);
+ 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_features(xen_session *session, struct xen_cpu_feature_set **result, xen_host_cpu host_cpu)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = host_cpu }
+ };
+
+ abstract_type result_type = xen_cpu_feature_set_abstract_type_;
+
+ *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_uuid(xen_session *session, char **result, xen_host_cpu host_cpu)
+{
+ *result = session->ok ? xen_strdup_((char *)host_cpu) : NULL;
+ 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 0000000000..edfcb21ac2
--- /dev/null
+++ b/tools/libxen/src/xen_int_float_map.c
@@ -0,0 +1,37 @@
+/*
+ * 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
+ */
+
+
+#include "xen_common.h"
+#include "xen_int_float_map.h"
+#include "xen_internal.h"
+
+
+xen_int_float_map *
+xen_int_float_map_alloc(size_t size)
+{
+ return calloc(1, sizeof(xen_int_float_map) +
+ size * sizeof(struct xen_int_float_map_contents));
+}
+
+
+void
+xen_int_float_map_free(xen_int_float_map *map)
+{
+ free(map);
+}
diff --git a/tools/libxen/src/xen_network.c b/tools/libxen/src/xen_network.c
new file mode 100644
index 0000000000..4c56e6e41d
--- /dev/null
+++ b/tools/libxen/src/xen_network.c
@@ -0,0 +1,364 @@
+/*
+ * 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
+ */
+
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "xen_common.h"
+#include "xen_internal.h"
+#include "xen_network.h"
+#include "xen_pif.h"
+#include "xen_vif.h"
+
+
+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 = "default_gateway",
+ .type = &abstract_type_string,
+ .offset = offsetof(xen_network_record, default_gateway) },
+ { .key = "default_netmask",
+ .type = &abstract_type_string,
+ .offset = offsetof(xen_network_record, default_netmask) }
+ };
+
+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);
+ free(record->default_gateway);
+ free(record->default_netmask);
+ 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_default_gateway(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_default_gateway");
+ return session->ok;
+}
+
+
+bool
+xen_network_get_default_netmask(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_default_netmask");
+ 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_default_gateway(xen_session *session, xen_network network, char *default_gateway)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = network },
+ { .type = &abstract_type_string,
+ .u.string_val = default_gateway }
+ };
+
+ xen_call_(session, "network.set_default_gateway", param_values, 2, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_network_set_default_netmask(xen_session *session, xen_network network, char *default_netmask)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = network },
+ { .type = &abstract_type_string,
+ .u.string_val = default_netmask }
+ };
+
+ xen_call_(session, "network.set_default_netmask", 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)
+{
+ *result = session->ok ? xen_strdup_((char *)network) : NULL;
+ 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 0000000000..cb1d0deca9
--- /dev/null
+++ b/tools/libxen/src/xen_on_crash_behaviour.c
@@ -0,0 +1,85 @@
+/*
+ * 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
+ */
+
+#include <string.h>
+
+#include "xen_internal.h"
+#include "xen_on_crash_behaviour.h"
+#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 0000000000..3cfe09766b
--- /dev/null
+++ b/tools/libxen/src/xen_on_normal_exit.c
@@ -0,0 +1,81 @@
+/*
+ * 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
+ */
+
+#include <string.h>
+
+#include "xen_internal.h"
+#include "xen_on_normal_exit.h"
+#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_pif.c b/tools/libxen/src/xen_pif.c
new file mode 100644
index 0000000000..b3edd91e6d
--- /dev/null
+++ b/tools/libxen/src/xen_pif.c
@@ -0,0 +1,403 @@
+/*
+ * 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
+ */
+
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "xen_common.h"
+#include "xen_host.h"
+#include "xen_internal.h"
+#include "xen_network.h"
+#include "xen_pif.h"
+
+
+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 = "name",
+ .type = &abstract_type_string,
+ .offset = offsetof(xen_pif_record, name) },
+ { .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_string,
+ .offset = offsetof(xen_pif_record, vlan) },
+ { .key = "io_read_kbs",
+ .type = &abstract_type_float,
+ .offset = offsetof(xen_pif_record, io_read_kbs) },
+ { .key = "io_write_kbs",
+ .type = &abstract_type_float,
+ .offset = offsetof(xen_pif_record, io_write_kbs) }
+ };
+
+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->name);
+ xen_network_record_opt_free(record->network);
+ xen_host_record_opt_free(record->host);
+ free(record->mac);
+ free(record->vlan);
+ 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_create(xen_session *session, xen_pif *result, xen_pif_record *record)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &xen_pif_record_abstract_type_,
+ .u.struct_val = record }
+ };
+
+ abstract_type result_type = abstract_type_string;
+
+ *result = NULL;
+ XEN_CALL_("PIF.create");
+ return session->ok;
+}
+
+
+bool
+xen_pif_destroy(xen_session *session, xen_pif pif)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = pif }
+ };
+
+ xen_call_(session, "PIF.destroy", param_values, 1, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_pif_get_name(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_name");
+ 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, 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_VLAN");
+ return session->ok;
+}
+
+
+bool
+xen_pif_get_io_read_kbs(xen_session *session, double *result, xen_pif pif)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = pif }
+ };
+
+ abstract_type result_type = abstract_type_float;
+
+ XEN_CALL_("PIF.get_io_read_kbs");
+ return session->ok;
+}
+
+
+bool
+xen_pif_get_io_write_kbs(xen_session *session, double *result, xen_pif pif)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = pif }
+ };
+
+ abstract_type result_type = abstract_type_float;
+
+ XEN_CALL_("PIF.get_io_write_kbs");
+ return session->ok;
+}
+
+
+bool
+xen_pif_set_name(xen_session *session, xen_pif pif, char *name)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = pif },
+ { .type = &abstract_type_string,
+ .u.string_val = name }
+ };
+
+ xen_call_(session, "PIF.set_name", param_values, 2, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_pif_set_network(xen_session *session, xen_pif pif, xen_network network)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = pif },
+ { .type = &abstract_type_string,
+ .u.string_val = network }
+ };
+
+ xen_call_(session, "PIF.set_network", param_values, 2, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_pif_set_host(xen_session *session, xen_pif pif, xen_host host)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = pif },
+ { .type = &abstract_type_string,
+ .u.string_val = host }
+ };
+
+ xen_call_(session, "PIF.set_host", 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, char *vlan)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = pif },
+ { .type = &abstract_type_string,
+ .u.string_val = vlan }
+ };
+
+ xen_call_(session, "PIF.set_VLAN", param_values, 2, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_pif_get_uuid(xen_session *session, char **result, xen_pif pif)
+{
+ *result = session->ok ? xen_strdup_((char *)pif) : NULL;
+ return session->ok;
+}
diff --git a/tools/libxen/src/xen_sr.c b/tools/libxen/src/xen_sr.c
new file mode 100644
index 0000000000..3c4ffb16de
--- /dev/null
+++ b/tools/libxen/src/xen_sr.c
@@ -0,0 +1,388 @@
+/*
+ * 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
+ */
+
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "xen_common.h"
+#include "xen_internal.h"
+#include "xen_sr.h"
+#include "xen_vdi.h"
+
+
+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 = "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 = "location",
+ .type = &abstract_type_string,
+ .offset = offsetof(xen_sr_record, location) }
+ };
+
+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);
+ free(record->type);
+ free(record->location);
+ 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_create(xen_session *session, xen_sr *result, xen_sr_record *record)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &xen_sr_record_abstract_type_,
+ .u.struct_val = record }
+ };
+
+ abstract_type result_type = abstract_type_string;
+
+ *result = NULL;
+ XEN_CALL_("SR.create");
+ return session->ok;
+}
+
+
+bool
+xen_sr_destroy(xen_session *session, xen_sr sr)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = sr }
+ };
+
+ xen_call_(session, "SR.destroy", param_values, 1, NULL, NULL);
+ 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_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_location(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_location");
+ 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_clone(xen_session *session, xen_sr *result, xen_sr sr, char *loc, char *name)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = sr },
+ { .type = &abstract_type_string,
+ .u.string_val = loc },
+ { .type = &abstract_type_string,
+ .u.string_val = name }
+ };
+
+ abstract_type result_type = abstract_type_string;
+
+ *result = NULL;
+ XEN_CALL_("SR.clone");
+ 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)
+{
+ *result = session->ok ? xen_strdup_((char *)sr) : NULL;
+ return session->ok;
+}
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 0000000000..34f5ec4d70
--- /dev/null
+++ b/tools/libxen/src/xen_string_string_map.c
@@ -0,0 +1,49 @@
+/*
+ * 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
+ */
+
+
+#include "xen_common.h"
+#include "xen_internal.h"
+#include "xen_string_string_map.h"
+
+
+xen_string_string_map *
+xen_string_string_map_alloc(size_t size)
+{
+ return calloc(1, sizeof(xen_string_string_map) +
+ size * sizeof(struct xen_string_string_map_contents));
+}
+
+
+void
+xen_string_string_map_free(xen_string_string_map *map)
+{
+ if (map == NULL)
+ {
+ return;
+ }
+
+ size_t 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 0000000000..35b80e2f66
--- /dev/null
+++ b/tools/libxen/src/xen_user.c
@@ -0,0 +1,201 @@
+/*
+ * 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
+ */
+
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "xen_common.h"
+#include "xen_internal.h"
+#include "xen_user.h"
+
+
+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)
+{
+ *result = session->ok ? xen_strdup_((char *)user) : NULL;
+ return session->ok;
+}
diff --git a/tools/libxen/src/xen_vbd.c b/tools/libxen/src/xen_vbd.c
new file mode 100644
index 0000000000..c49ecf236a
--- /dev/null
+++ b/tools/libxen/src/xen_vbd.c
@@ -0,0 +1,387 @@
+/*
+ * 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
+ */
+
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "xen_common.h"
+#include "xen_driver_type_internal.h"
+#include "xen_internal.h"
+#include "xen_vbd.h"
+#include "xen_vbd_mode_internal.h"
+#include "xen_vdi.h"
+#include "xen_vm.h"
+
+
+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 = "image",
+ .type = &abstract_type_string,
+ .offset = offsetof(xen_vbd_record, image) },
+ { .key = "mode",
+ .type = &xen_vbd_mode_abstract_type_,
+ .offset = offsetof(xen_vbd_record, mode) },
+ { .key = "driver",
+ .type = &xen_driver_type_abstract_type_,
+ .offset = offsetof(xen_vbd_record, driver) },
+ { .key = "io_read_kbs",
+ .type = &abstract_type_float,
+ .offset = offsetof(xen_vbd_record, io_read_kbs) },
+ { .key = "io_write_kbs",
+ .type = &abstract_type_float,
+ .offset = offsetof(xen_vbd_record, io_write_kbs) }
+ };
+
+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);
+}
+
+
+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_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_;
+ char *result_str = NULL;
+ XEN_CALL_("VBD.get_mode");
+ *result = xen_vbd_mode_from_string(session, result_str);
+ return session->ok;
+}
+
+
+bool
+xen_vbd_get_driver(xen_session *session, enum xen_driver_type *result, xen_vbd vbd)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vbd }
+ };
+
+ abstract_type result_type = xen_driver_type_abstract_type_;
+ char *result_str = NULL;
+ XEN_CALL_("VBD.get_driver");
+ *result = xen_driver_type_from_string(session, result_str);
+ return session->ok;
+}
+
+
+bool
+xen_vbd_get_io_read_kbs(xen_session *session, double *result, xen_vbd vbd)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vbd }
+ };
+
+ abstract_type result_type = abstract_type_float;
+
+ XEN_CALL_("VBD.get_io_read_kbs");
+ return session->ok;
+}
+
+
+bool
+xen_vbd_get_io_write_kbs(xen_session *session, double *result, xen_vbd vbd)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vbd }
+ };
+
+ abstract_type result_type = abstract_type_float;
+
+ XEN_CALL_("VBD.get_io_write_kbs");
+ return session->ok;
+}
+
+
+bool
+xen_vbd_set_vm(xen_session *session, xen_vbd vbd, xen_vm vm)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vbd },
+ { .type = &abstract_type_string,
+ .u.string_val = vm }
+ };
+
+ xen_call_(session, "VBD.set_VM", param_values, 2, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_vbd_set_vdi(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.set_VDI", param_values, 2, NULL, NULL);
+ 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_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.string_val = xen_vbd_mode_to_string(mode) }
+ };
+
+ xen_call_(session, "VBD.set_mode", param_values, 2, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_vbd_set_driver(xen_session *session, xen_vbd vbd, enum xen_driver_type driver)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vbd },
+ { .type = &xen_driver_type_abstract_type_,
+ .u.string_val = xen_driver_type_to_string(driver) }
+ };
+
+ xen_call_(session, "VBD.set_driver", 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_get_uuid(xen_session *session, char **result, xen_vbd vbd)
+{
+ *result = session->ok ? xen_strdup_((char *)vbd) : NULL;
+ 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 0000000000..e9fa71140a
--- /dev/null
+++ b/tools/libxen/src/xen_vbd_mode.c
@@ -0,0 +1,81 @@
+/*
+ * 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
+ */
+
+#include <string.h>
+
+#include "xen_internal.h"
+#include "xen_vbd_mode.h"
+#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_vdi.c b/tools/libxen/src/xen_vdi.c
new file mode 100644
index 0000000000..a8d157d232
--- /dev/null
+++ b/tools/libxen/src/xen_vdi.c
@@ -0,0 +1,533 @@
+/*
+ * 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
+ */
+
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "xen_common.h"
+#include "xen_internal.h"
+#include "xen_sr.h"
+#include "xen_vbd.h"
+#include "xen_vdi.h"
+#include "xen_vdi_type_internal.h"
+
+
+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 = "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 = "sector_size",
+ .type = &abstract_type_int,
+ .offset = offsetof(xen_vdi_record, sector_size) },
+ { .key = "type",
+ .type = &xen_vdi_type_abstract_type_,
+ .offset = offsetof(xen_vdi_record, type) },
+ { .key = "parent",
+ .type = &abstract_type_ref,
+ .offset = offsetof(xen_vdi_record, parent) },
+ { .key = "children",
+ .type = &abstract_type_ref_set,
+ .offset = offsetof(xen_vdi_record, children) },
+ { .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) }
+ };
+
+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_vdi_record_opt_free(record->parent);
+ xen_vdi_record_opt_set_free(record->children);
+ 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_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_sector_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_sector_size");
+ 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_;
+ char *result_str = NULL;
+ XEN_CALL_("VDI.get_type");
+ *result = xen_vdi_type_from_string(session, result_str);
+ return session->ok;
+}
+
+
+bool
+xen_vdi_get_parent(xen_session *session, xen_vdi *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_parent");
+ return session->ok;
+}
+
+
+bool
+xen_vdi_get_children(xen_session *session, struct xen_vdi_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_children");
+ 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_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_sr(xen_session *session, xen_vdi vdi, xen_sr sr)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vdi },
+ { .type = &abstract_type_string,
+ .u.string_val = sr }
+ };
+
+ xen_call_(session, "VDI.set_SR", 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_snapshot(xen_session *session, xen_vdi *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.snapshot");
+ return session->ok;
+}
+
+
+bool
+xen_vdi_resize(xen_session *session, xen_vdi vdi, int64_t size)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vdi },
+ { .type = &abstract_type_int,
+ .u.int_val = size }
+ };
+
+ xen_call_(session, "VDI.resize", param_values, 2, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_vdi_get_uuid(xen_session *session, char **result, xen_vdi vdi)
+{
+ *result = session->ok ? xen_strdup_((char *)vdi) : NULL;
+ 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 0000000000..90cfe8c820
--- /dev/null
+++ b/tools/libxen/src/xen_vdi_type.c
@@ -0,0 +1,82 @@
+/*
+ * 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
+ */
+
+#include <string.h>
+
+#include "xen_internal.h"
+#include "xen_vdi_type.h"
+#include "xen_vdi_type_internal.h"
+
+
+/*
+ * Maintain this in the same order as the enum declaration!
+ */
+static const char *lookup_table[] =
+{
+ "system",
+ "user",
+ "ephemeral"
+};
+
+
+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 0000000000..ce28626150
--- /dev/null
+++ b/tools/libxen/src/xen_vif.c
@@ -0,0 +1,440 @@
+/*
+ * 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
+ */
+
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "xen_common.h"
+#include "xen_driver_type_internal.h"
+#include "xen_internal.h"
+#include "xen_network.h"
+#include "xen_vif.h"
+#include "xen_vm.h"
+
+
+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 = "name",
+ .type = &abstract_type_string,
+ .offset = offsetof(xen_vif_record, name) },
+ { .key = "type",
+ .type = &xen_driver_type_abstract_type_,
+ .offset = offsetof(xen_vif_record, type) },
+ { .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 = "io_read_kbs",
+ .type = &abstract_type_float,
+ .offset = offsetof(xen_vif_record, io_read_kbs) },
+ { .key = "io_write_kbs",
+ .type = &abstract_type_float,
+ .offset = offsetof(xen_vif_record, io_write_kbs) }
+ };
+
+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->name);
+ free(record->device);
+ xen_network_record_opt_free(record->network);
+ xen_vm_record_opt_free(record->vm);
+ free(record->mac);
+ 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_name(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_name");
+ return session->ok;
+}
+
+
+bool
+xen_vif_get_type(xen_session *session, enum xen_driver_type *result, xen_vif vif)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vif }
+ };
+
+ abstract_type result_type = xen_driver_type_abstract_type_;
+ char *result_str = NULL;
+ XEN_CALL_("VIF.get_type");
+ *result = xen_driver_type_from_string(session, result_str);
+ 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_io_read_kbs(xen_session *session, double *result, xen_vif vif)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vif }
+ };
+
+ abstract_type result_type = abstract_type_float;
+
+ XEN_CALL_("VIF.get_io_read_kbs");
+ return session->ok;
+}
+
+
+bool
+xen_vif_get_io_write_kbs(xen_session *session, double *result, xen_vif vif)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vif }
+ };
+
+ abstract_type result_type = abstract_type_float;
+
+ XEN_CALL_("VIF.get_io_write_kbs");
+ return session->ok;
+}
+
+
+bool
+xen_vif_set_name(xen_session *session, xen_vif vif, char *name)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vif },
+ { .type = &abstract_type_string,
+ .u.string_val = name }
+ };
+
+ xen_call_(session, "VIF.set_name", param_values, 2, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_vif_set_type(xen_session *session, xen_vif vif, enum xen_driver_type type)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vif },
+ { .type = &xen_driver_type_abstract_type_,
+ .u.string_val = xen_driver_type_to_string(type) }
+ };
+
+ xen_call_(session, "VIF.set_type", param_values, 2, NULL, NULL);
+ 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_network(xen_session *session, xen_vif vif, xen_network network)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vif },
+ { .type = &abstract_type_string,
+ .u.string_val = network }
+ };
+
+ xen_call_(session, "VIF.set_network", param_values, 2, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_vif_set_vm(xen_session *session, xen_vif vif, xen_vm vm)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vif },
+ { .type = &abstract_type_string,
+ .u.string_val = vm }
+ };
+
+ xen_call_(session, "VIF.set_VM", 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_get_uuid(xen_session *session, char **result, xen_vif vif)
+{
+ *result = session->ok ? xen_strdup_((char *)vif) : NULL;
+ return session->ok;
+}
diff --git a/tools/libxen/src/xen_vm.c b/tools/libxen/src/xen_vm.c
new file mode 100644
index 0000000000..cd0271c71c
--- /dev/null
+++ b/tools/libxen/src/xen_vm.c
@@ -0,0 +1,1596 @@
+/*
+ * 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
+ */
+
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "xen_boot_type_internal.h"
+#include "xen_common.h"
+#include "xen_cpu_feature.h"
+#include "xen_cpu_feature_internal.h"
+#include "xen_host.h"
+#include "xen_int_float_map.h"
+#include "xen_internal.h"
+#include "xen_on_crash_behaviour_internal.h"
+#include "xen_on_normal_exit_internal.h"
+#include "xen_string_string_map.h"
+#include "xen_vbd.h"
+#include "xen_vif.h"
+#include "xen_vm.h"
+#include "xen_vm_power_state_internal.h"
+#include "xen_vtpm.h"
+
+
+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 = "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_actual",
+ .type = &abstract_type_int,
+ .offset = offsetof(xen_vm_record, memory_actual) },
+ { .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_policy",
+ .type = &abstract_type_string,
+ .offset = offsetof(xen_vm_record, vcpus_policy) },
+ { .key = "vcpus_params",
+ .type = &abstract_type_string,
+ .offset = offsetof(xen_vm_record, vcpus_params) },
+ { .key = "vcpus_number",
+ .type = &abstract_type_int,
+ .offset = offsetof(xen_vm_record, vcpus_number) },
+ { .key = "vcpus_utilisation",
+ .type = &abstract_type_int_float_map,
+ .offset = offsetof(xen_vm_record, vcpus_utilisation) },
+ { .key = "vcpus_features_required",
+ .type = &xen_cpu_feature_set_abstract_type_,
+ .offset = offsetof(xen_vm_record, vcpus_features_required) },
+ { .key = "vcpus_features_can_use",
+ .type = &xen_cpu_feature_set_abstract_type_,
+ .offset = offsetof(xen_vm_record, vcpus_features_can_use) },
+ { .key = "vcpus_features_force_on",
+ .type = &xen_cpu_feature_set_abstract_type_,
+ .offset = offsetof(xen_vm_record, vcpus_features_force_on) },
+ { .key = "vcpus_features_force_off",
+ .type = &xen_cpu_feature_set_abstract_type_,
+ .offset = offsetof(xen_vm_record, vcpus_features_force_off) },
+ { .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_suspend",
+ .type = &xen_on_normal_exit_abstract_type_,
+ .offset = offsetof(xen_vm_record, actions_after_suspend) },
+ { .key = "actions_after_crash",
+ .type = &xen_on_crash_behaviour_abstract_type_,
+ .offset = offsetof(xen_vm_record, actions_after_crash) },
+ { .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 = "VTPMs",
+ .type = &abstract_type_ref_set,
+ .offset = offsetof(xen_vm_record, vtpms) },
+ { .key = "bios_boot",
+ .type = &abstract_type_string,
+ .offset = offsetof(xen_vm_record, bios_boot) },
+ { .key = "platform_std_VGA",
+ .type = &abstract_type_bool,
+ .offset = offsetof(xen_vm_record, platform_std_vga) },
+ { .key = "platform_serial",
+ .type = &abstract_type_string,
+ .offset = offsetof(xen_vm_record, platform_serial) },
+ { .key = "platform_localtime",
+ .type = &abstract_type_bool,
+ .offset = offsetof(xen_vm_record, platform_localtime) },
+ { .key = "platform_clock_offset",
+ .type = &abstract_type_bool,
+ .offset = offsetof(xen_vm_record, platform_clock_offset) },
+ { .key = "platform_enable_audio",
+ .type = &abstract_type_bool,
+ .offset = offsetof(xen_vm_record, platform_enable_audio) },
+ { .key = "builder",
+ .type = &abstract_type_string,
+ .offset = offsetof(xen_vm_record, builder) },
+ { .key = "boot_method",
+ .type = &xen_boot_type_abstract_type_,
+ .offset = offsetof(xen_vm_record, boot_method) },
+ { .key = "kernel_kernel",
+ .type = &abstract_type_string,
+ .offset = offsetof(xen_vm_record, kernel_kernel) },
+ { .key = "kernel_initrd",
+ .type = &abstract_type_string,
+ .offset = offsetof(xen_vm_record, kernel_initrd) },
+ { .key = "kernel_args",
+ .type = &abstract_type_string,
+ .offset = offsetof(xen_vm_record, kernel_args) },
+ { .key = "grub_cmdline",
+ .type = &abstract_type_string,
+ .offset = offsetof(xen_vm_record, grub_cmdline) },
+ { .key = "PCI_bus",
+ .type = &abstract_type_string,
+ .offset = offsetof(xen_vm_record, pci_bus) },
+ { .key = "tools_version",
+ .type = &abstract_type_string_string_map,
+ .offset = offsetof(xen_vm_record, tools_version) },
+ { .key = "otherConfig",
+ .type = &abstract_type_string_string_map,
+ .offset = offsetof(xen_vm_record, otherconfig) }
+ };
+
+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_host_record_opt_free(record->resident_on);
+ free(record->vcpus_policy);
+ free(record->vcpus_params);
+ xen_int_float_map_free(record->vcpus_utilisation);
+ xen_cpu_feature_set_free(record->vcpus_features_required);
+ xen_cpu_feature_set_free(record->vcpus_features_can_use);
+ xen_cpu_feature_set_free(record->vcpus_features_force_on);
+ xen_cpu_feature_set_free(record->vcpus_features_force_off);
+ xen_vif_record_opt_set_free(record->vifs);
+ xen_vbd_record_opt_set_free(record->vbds);
+ xen_vtpm_record_opt_set_free(record->vtpms);
+ free(record->bios_boot);
+ free(record->platform_serial);
+ free(record->builder);
+ free(record->kernel_kernel);
+ free(record->kernel_initrd);
+ free(record->kernel_args);
+ free(record->grub_cmdline);
+ free(record->pci_bus);
+ xen_string_string_map_free(record->tools_version);
+ xen_string_string_map_free(record->otherconfig);
+ 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_;
+ char *result_str = NULL;
+ XEN_CALL_("VM.get_power_state");
+ *result = xen_vm_power_state_from_string(session, result_str);
+ 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_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_actual(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_actual");
+ 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_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_VCPUs_policy");
+ return session->ok;
+}
+
+
+bool
+xen_vm_get_vcpus_params(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_VCPUs_params");
+ return session->ok;
+}
+
+
+bool
+xen_vm_get_vcpus_number(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_number");
+ return session->ok;
+}
+
+
+bool
+xen_vm_get_vcpus_utilisation(xen_session *session, xen_int_float_map **result, xen_vm vm)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vm }
+ };
+
+ abstract_type result_type = abstract_type_int_float_map;
+
+ *result = NULL;
+ XEN_CALL_("VM.get_VCPUs_utilisation");
+ return session->ok;
+}
+
+
+bool
+xen_vm_get_vcpus_features_required(xen_session *session, struct xen_cpu_feature_set **result, xen_vm vm)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vm }
+ };
+
+ abstract_type result_type = xen_cpu_feature_set_abstract_type_;
+
+ *result = NULL;
+ XEN_CALL_("VM.get_VCPUs_features_required");
+ return session->ok;
+}
+
+
+bool
+xen_vm_get_vcpus_features_can_use(xen_session *session, struct xen_cpu_feature_set **result, xen_vm vm)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vm }
+ };
+
+ abstract_type result_type = xen_cpu_feature_set_abstract_type_;
+
+ *result = NULL;
+ XEN_CALL_("VM.get_VCPUs_features_can_use");
+ return session->ok;
+}
+
+
+bool
+xen_vm_get_vcpus_features_force_on(xen_session *session, struct xen_cpu_feature_set **result, xen_vm vm)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vm }
+ };
+
+ abstract_type result_type = xen_cpu_feature_set_abstract_type_;
+
+ *result = NULL;
+ XEN_CALL_("VM.get_VCPUs_features_force_on");
+ return session->ok;
+}
+
+
+bool
+xen_vm_get_vcpus_features_force_off(xen_session *session, struct xen_cpu_feature_set **result, xen_vm vm)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vm }
+ };
+
+ abstract_type result_type = xen_cpu_feature_set_abstract_type_;
+
+ *result = NULL;
+ XEN_CALL_("VM.get_VCPUs_features_force_off");
+ 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_;
+ char *result_str = NULL;
+ XEN_CALL_("VM.get_actions_after_shutdown");
+ *result = xen_on_normal_exit_from_string(session, result_str);
+ 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_;
+ char *result_str = NULL;
+ XEN_CALL_("VM.get_actions_after_reboot");
+ *result = xen_on_normal_exit_from_string(session, result_str);
+ return session->ok;
+}
+
+
+bool
+xen_vm_get_actions_after_suspend(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_;
+ char *result_str = NULL;
+ XEN_CALL_("VM.get_actions_after_suspend");
+ *result = xen_on_normal_exit_from_string(session, result_str);
+ 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_;
+ char *result_str = NULL;
+ XEN_CALL_("VM.get_actions_after_crash");
+ *result = xen_on_crash_behaviour_from_string(session, result_str);
+ 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_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_bios_boot(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_bios_boot");
+ return session->ok;
+}
+
+
+bool
+xen_vm_get_platform_std_vga(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_platform_std_VGA");
+ return session->ok;
+}
+
+
+bool
+xen_vm_get_platform_serial(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_platform_serial");
+ return session->ok;
+}
+
+
+bool
+xen_vm_get_platform_localtime(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_platform_localtime");
+ return session->ok;
+}
+
+
+bool
+xen_vm_get_platform_clock_offset(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_platform_clock_offset");
+ return session->ok;
+}
+
+
+bool
+xen_vm_get_platform_enable_audio(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_platform_enable_audio");
+ return session->ok;
+}
+
+
+bool
+xen_vm_get_builder(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_builder");
+ return session->ok;
+}
+
+
+bool
+xen_vm_get_boot_method(xen_session *session, enum xen_boot_type *result, xen_vm vm)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vm }
+ };
+
+ abstract_type result_type = xen_boot_type_abstract_type_;
+ char *result_str = NULL;
+ XEN_CALL_("VM.get_boot_method");
+ *result = xen_boot_type_from_string(session, result_str);
+ return session->ok;
+}
+
+
+bool
+xen_vm_get_kernel_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_kernel_kernel");
+ return session->ok;
+}
+
+
+bool
+xen_vm_get_kernel_initrd(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_kernel_initrd");
+ return session->ok;
+}
+
+
+bool
+xen_vm_get_kernel_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_kernel_args");
+ return session->ok;
+}
+
+
+bool
+xen_vm_get_grub_cmdline(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_grub_cmdline");
+ 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_tools_version(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_tools_version");
+ return session->ok;
+}
+
+
+bool
+xen_vm_get_otherconfig(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_otherConfig");
+ 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_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_vcpus_policy(xen_session *session, xen_vm vm, char *policy)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vm },
+ { .type = &abstract_type_string,
+ .u.string_val = policy }
+ };
+
+ xen_call_(session, "VM.set_vcpus_policy", param_values, 2, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_vm_set_vcpus_params(xen_session *session, xen_vm vm, char *params)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vm },
+ { .type = &abstract_type_string,
+ .u.string_val = params }
+ };
+
+ xen_call_(session, "VM.set_vcpus_params", param_values, 2, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_vm_set_vcpus_features_force_on(xen_session *session, xen_vm vm, struct xen_cpu_feature_set *force_on)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vm },
+ { .type = &xen_cpu_feature_set_abstract_type_,
+ .u.set_val = (arbitrary_set *)force_on }
+ };
+
+ xen_call_(session, "VM.set_vcpus_features_force_on", param_values, 2, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_vm_set_vcpus_features_force_off(xen_session *session, xen_vm vm, struct xen_cpu_feature_set *force_off)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vm },
+ { .type = &xen_cpu_feature_set_abstract_type_,
+ .u.set_val = (arbitrary_set *)force_off }
+ };
+
+ xen_call_(session, "VM.set_vcpus_features_force_off", 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.string_val = xen_on_normal_exit_to_string(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.string_val = xen_on_normal_exit_to_string(after_reboot) }
+ };
+
+ xen_call_(session, "VM.set_actions_after_reboot", param_values, 2, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_vm_set_actions_after_suspend(xen_session *session, xen_vm vm, enum xen_on_normal_exit after_suspend)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vm },
+ { .type = &xen_on_normal_exit_abstract_type_,
+ .u.string_val = xen_on_normal_exit_to_string(after_suspend) }
+ };
+
+ xen_call_(session, "VM.set_actions_after_suspend", 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.string_val = xen_on_crash_behaviour_to_string(after_crash) }
+ };
+
+ xen_call_(session, "VM.set_actions_after_crash", param_values, 2, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_vm_set_bios_boot(xen_session *session, xen_vm vm, char *boot)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vm },
+ { .type = &abstract_type_string,
+ .u.string_val = boot }
+ };
+
+ xen_call_(session, "VM.set_bios_boot", param_values, 2, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_vm_set_platform_std_vga(xen_session *session, xen_vm vm, bool std_vga)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vm },
+ { .type = &abstract_type_bool,
+ .u.bool_val = std_vga }
+ };
+
+ xen_call_(session, "VM.set_platform_std_vga", param_values, 2, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_vm_set_platform_serial(xen_session *session, xen_vm vm, char *serial)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vm },
+ { .type = &abstract_type_string,
+ .u.string_val = serial }
+ };
+
+ xen_call_(session, "VM.set_platform_serial", param_values, 2, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_vm_set_platform_localtime(xen_session *session, xen_vm vm, bool localtime)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vm },
+ { .type = &abstract_type_bool,
+ .u.bool_val = localtime }
+ };
+
+ xen_call_(session, "VM.set_platform_localtime", param_values, 2, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_vm_set_platform_clock_offset(xen_session *session, xen_vm vm, bool clock_offset)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vm },
+ { .type = &abstract_type_bool,
+ .u.bool_val = clock_offset }
+ };
+
+ xen_call_(session, "VM.set_platform_clock_offset", param_values, 2, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_vm_set_platform_enable_audio(xen_session *session, xen_vm vm, bool enable_audio)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vm },
+ { .type = &abstract_type_bool,
+ .u.bool_val = enable_audio }
+ };
+
+ xen_call_(session, "VM.set_platform_enable_audio", param_values, 2, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_vm_set_builder(xen_session *session, xen_vm vm, char *builder)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vm },
+ { .type = &abstract_type_string,
+ .u.string_val = builder }
+ };
+
+ xen_call_(session, "VM.set_builder", param_values, 2, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_vm_set_boot_method(xen_session *session, xen_vm vm, enum xen_boot_type boot_method)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vm },
+ { .type = &xen_boot_type_abstract_type_,
+ .u.string_val = xen_boot_type_to_string(boot_method) }
+ };
+
+ xen_call_(session, "VM.set_boot_method", param_values, 2, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_vm_set_kernel_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_kernel_kernel", param_values, 2, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_vm_set_kernel_initrd(xen_session *session, xen_vm vm, char *initrd)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vm },
+ { .type = &abstract_type_string,
+ .u.string_val = initrd }
+ };
+
+ xen_call_(session, "VM.set_kernel_initrd", param_values, 2, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_vm_set_kernel_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_kernel_args", param_values, 2, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_vm_set_grub_cmdline(xen_session *session, xen_vm vm, char *cmdline)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vm },
+ { .type = &abstract_type_string,
+ .u.string_val = cmdline }
+ };
+
+ xen_call_(session, "VM.set_grub_cmdline", param_values, 2, NULL, NULL);
+ return session->ok;
+}
+
+
+bool
+xen_vm_set_otherconfig(xen_session *session, xen_vm vm, xen_string_string_map *otherconfig)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vm },
+ { .type = &abstract_type_string_string_map,
+ .u.set_val = (arbitrary_set *)otherconfig }
+ };
+
+ xen_call_(session, "VM.set_otherconfig", 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_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)
+{
+ *result = session->ok ? xen_strdup_((char *)vm) : NULL;
+ 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 0000000000..a9e2545c47
--- /dev/null
+++ b/tools/libxen/src/xen_vm_power_state.c
@@ -0,0 +1,85 @@
+/*
+ * 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
+ */
+
+#include <string.h>
+
+#include "xen_internal.h"
+#include "xen_vm_power_state.h"
+#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",
+ "ShuttingDown",
+ "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 0000000000..eb8156a0a3
--- /dev/null
+++ b/tools/libxen/src/xen_vtpm.c
@@ -0,0 +1,227 @@
+/*
+ * 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 <stddef.h>
+#include <stdlib.h>
+
+#include "xen_common.h"
+#include "xen_driver_type_internal.h"
+#include "xen_internal.h"
+#include "xen_vm.h"
+#include "xen_vtpm.h"
+
+
+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 = "driver",
+ .type = &xen_driver_type_abstract_type_,
+ .offset = offsetof(xen_vtpm_record, driver) },
+ { .key = "instance",
+ .type = &abstract_type_int,
+ .offset = offsetof(xen_vtpm_record, instance) }
+ };
+
+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);
+ 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_driver(xen_session *session, enum xen_driver_type *result, xen_vtpm vtpm)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vtpm }
+ };
+
+ abstract_type result_type = xen_driver_type_abstract_type_;
+ char *result_str = NULL;
+ XEN_CALL_("VTPM.get_driver");
+ *result = xen_driver_type_from_string(session, result_str);
+ return session->ok;
+}
+
+
+bool
+xen_vtpm_get_instance(xen_session *session, int64_t *result, xen_vtpm vtpm)
+{
+ abstract_value param_values[] =
+ {
+ { .type = &abstract_type_string,
+ .u.string_val = vtpm }
+ };
+
+ abstract_type result_type = abstract_type_int;
+
+ XEN_CALL_("VTPM.get_instance");
+ return session->ok;
+}
+
+
+bool
+xen_vtpm_get_uuid(xen_session *session, char **result, xen_vtpm vtpm)
+{
+ *result = session->ok ? xen_strdup_((char *)vtpm) : NULL;
+ return session->ok;
+}
diff --git a/tools/libxen/test/test_bindings.c b/tools/libxen/test/test_bindings.c
new file mode 100644
index 0000000000..0c47305a09
--- /dev/null
+++ b/tools/libxen/test/test_bindings.c
@@ -0,0 +1,424 @@
+/*
+ * 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
+ */
+
+#define _GNU_SOURCE
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <libxml/parser.h>
+#include <curl/curl.h>
+
+#include "xen_host.h"
+#include "xen_sr.h"
+#include "xen_vbd.h"
+#include "xen_vdi.h"
+#include "xen_vm.h"
+
+
+static void usage()
+{
+ fprintf(stderr,
+"Usage:\n"
+"\n"
+" test_bindings <url> <username> <password>\n"
+"\n"
+"where\n"
+" <url> is a fragment of the server's URL, e.g. localhost:8005/RPC2;\n"
+" <username> is the username to use at the server; and\n"
+" <password> is the password.\n");
+
+ exit(EXIT_FAILURE);
+}
+
+
+static char *url;
+
+
+typedef struct
+{
+ xen_result_func func;
+ void *handle;
+} xen_comms;
+
+
+static void create_new_vm(xen_session *session);
+
+
+static size_t
+write_func(void *ptr, size_t size, size_t nmemb, xen_comms *comms)
+{
+ size_t n = size * nmemb;
+ 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;
+
+ 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);
+
+ 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))
+ {
+ 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;
+ }
+
+ printf("%s.\n", vm_uuid);
+
+ fprintf(stderr, "In bytes, the VM UUID is ");
+ for (int i = 0; i < 15; i++)
+ {
+ fprintf(stderr, "%x, ", (unsigned int)vm_uuid_bytes[i]);
+ }
+ fprintf(stderr, "%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("%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));
+
+ for (size_t i = 0; i < vm_record->vcpus_utilisation->size; i++)
+ {
+ printf("%"PRId64" -> %lf.\n",
+ vm_record->vcpus_utilisation->contents[i].key,
+ vm_record->vcpus_utilisation->contents[i].val);
+ }
+
+ xen_uuid_bytes_free(vm_uuid_bytes);
+ xen_uuid_free(vm_uuid);
+ xen_vm_free(vm);
+
+ xen_vm_record_free(vm_record);
+
+ xen_host_free(host);
+ xen_string_string_map_free(versions);
+
+
+ create_new_vm(session);
+ if (!session->ok)
+ {
+ /* Error has been logged, just clean up. */
+ CLEANUP;
+ return 1;
+ }
+
+ 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 void create_new_vm(xen_session *session)
+{
+ xen_cpu_feature_set *empty_cpu_feature_set =
+ xen_cpu_feature_set_alloc(0);
+
+ xen_cpu_feature_set *force_off_cpu_feature_set =
+ xen_cpu_feature_set_alloc(1);
+ force_off_cpu_feature_set->contents[0] = XEN_CPU_FEATURE_MMX;
+
+ xen_vm_record vm_record =
+ {
+ .name_label = "NewVM",
+ .name_description = "New VM Description",
+ .user_version = 1,
+ .is_a_template = false,
+ .memory_static_max = 256,
+ .memory_dynamic_max = 256,
+ .memory_dynamic_min = 128,
+ .memory_static_min = 128,
+ .vcpus_policy = "credit",
+ .vcpus_params = "",
+ .vcpus_number = 2,
+ .vcpus_features_required = empty_cpu_feature_set,
+ .vcpus_features_can_use = empty_cpu_feature_set,
+ .vcpus_features_force_on = empty_cpu_feature_set,
+ .vcpus_features_force_off = force_off_cpu_feature_set,
+ .actions_after_shutdown = XEN_ON_NORMAL_EXIT_DESTROY,
+ .actions_after_reboot = XEN_ON_NORMAL_EXIT_RESTART,
+ .actions_after_suspend = XEN_ON_NORMAL_EXIT_DESTROY,
+ .actions_after_crash = XEN_ON_CRASH_BEHAVIOUR_PRESERVE,
+ .bios_boot = "hd(0,0)",
+ .builder = "Linux",
+ .boot_method = XEN_BOOT_TYPE_KERNEL_EXTERNAL,
+ .kernel_kernel = "vmlinuz",
+ .kernel_initrd = "initrd.img",
+ .kernel_args = ""
+ };
+
+
+ xen_vm vm;
+ xen_vm_create(session, &vm, &vm_record);
+
+ xen_cpu_feature_set_free(empty_cpu_feature_set);
+ xen_cpu_feature_set_free(force_off_cpu_feature_set);
+
+ if (!session->ok)
+ {
+ fprintf(stderr, "VM creation failed.\n");
+ print_error(session);
+ return;
+ }
+
+
+ /*
+ * Create a new disk for the new VM.
+ */
+ xen_sr_set *srs;
+ if (!xen_sr_get_by_name_label(session, &srs, "Local") ||
+ srs->size < 1)
+ {
+ fprintf(stderr, "SR lookup failed.\n");
+ print_error(session);
+ xen_vm_free(vm);
+ return;
+ }
+
+ 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 = (1 << 20) / 512,
+ .sector_size = 512,
+ .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;
+ }
+
+
+ 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 = "sda1",
+ .mode = XEN_VBD_MODE_RW,
+ .driver = XEN_DRIVER_TYPE_PARAVIRTUALISED
+ };
+
+ 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;
+ }
+
+ char *vm_uuid;
+ char *vdi0_uuid;
+ char *vbd0_uuid;
+
+ 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 (!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_vbd_free(vbd0);
+ xen_vdi_free(vdi0);
+ xen_sr_set_free(srs);
+ xen_vm_free(vm);
+ return;
+ }
+
+ fprintf(stderr,
+ "Created a new 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_vbd_free(vbd0);
+ xen_vdi_free(vdi0);
+ xen_sr_set_free(srs);
+ xen_vm_free(vm);
+}
diff --git a/tools/pygrub/setup.py b/tools/pygrub/setup.py
index a6a8d50d03..52dcf57373 100644
--- a/tools/pygrub/setup.py
+++ b/tools/pygrub/setup.py
@@ -3,48 +3,27 @@ from distutils.ccompiler import new_compiler
import os
import sys
-extra_compile_args = [ "-fno-strict-aliasing", "-Wall", "-Werror" ]
+extra_compile_args = [ "-fno-strict-aliasing", "-Werror" ]
-fsys_mods = []
-fsys_pkgs = []
+XEN_ROOT = "../.."
-if os.path.exists("/usr/include/ext2fs/ext2_fs.h"):
- ext2defines = []
- cc = new_compiler()
- cc.add_library("ext2fs")
- if hasattr(cc, "has_function") and cc.has_function("ext2fs_open2"):
- ext2defines.append( ("HAVE_EXT2FS_OPEN2", None) )
- else:
- sys.stderr.write("WARNING: older version of e2fsprogs installed, not building full\n")
- sys.stderr.write(" disk support for ext2.\n")
-
- ext2 = Extension("grub.fsys.ext2._pyext2",
- extra_compile_args = extra_compile_args,
- libraries = ["ext2fs"],
- define_macros = ext2defines,
- sources = ["src/fsys/ext2/ext2module.c"])
- fsys_mods.append(ext2)
- fsys_pkgs.append("grub.fsys.ext2")
+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"])
-if os.path.exists("/usr/include/reiserfs/reiserfs.h"):
- reiser = Extension("grub.fsys.reiser._pyreiser",
- extra_compile_args = extra_compile_args,
- libraries = ["reiserfs"],
- sources = ["src/fsys/reiser/reisermodule.c"])
- fsys_mods.append(reiser)
- fsys_pkgs.append("grub.fsys.reiser")
+pkgs = [ 'grub' ]
-pkgs = ['grub', 'grub.fsys']
-pkgs.extend(fsys_pkgs)
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'},
+ package_dir={'grub': 'src', 'fsimage': 'src'},
scripts = ["src/pygrub"],
packages=pkgs,
- ext_modules = fsys_mods
+ ext_modules = [ fsimage ]
)
-
diff --git a/tools/pygrub/src/fsimage/fsimage.c b/tools/pygrub/src/fsimage/fsimage.c
new file mode 100644
index 0000000000..ad0182d691
--- /dev/null
+++ b/tools/pygrub/src/fsimage/fsimage.c
@@ -0,0 +1,299 @@
+/*
+ * 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 <Python.h>
+
+#include <fsimage.h>
+#include <stdlib.h>
+
+#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);
+ PyMem_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);
+ PyMem_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", NULL };
+ char * name;
+ uint64_t offset = 0;
+ fsimage_fs_t *fs;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|L", kwlist,
+ &name, &offset))
+ return (NULL);
+
+ if ((fs = PyObject_NEW(fsimage_fs_t, &fsimage_fs_type)) == NULL)
+ return (NULL);
+
+ if ((fs->fs = fsi_open_fsimage(name, offset)) == NULL) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ return (NULL);
+ }
+
+ return (PyObject *)fs;
+}
+
+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");
+
+static struct PyMethodDef fsimage_module_methods[] = {
+ { "open", (PyCFunction)fsimage_open,
+ METH_VARARGS|METH_KEYWORDS, fsimage_open__doc__ },
+ { NULL, NULL, 0, NULL }
+};
+
+PyMODINIT_FUNC
+initfsimage(void)
+{
+ Py_InitModule("fsimage", fsimage_module_methods);
+}
diff --git a/tools/pygrub/src/fsys/__init__.py b/tools/pygrub/src/fsys/__init__.py
deleted file mode 100644
index 07e12c95b6..0000000000
--- a/tools/pygrub/src/fsys/__init__.py
+++ /dev/null
@@ -1,64 +0,0 @@
-#
-# Copyright 2005 Red Hat, Inc.
-# Jeremy Katz <katzj@redhat.com>
-#
-# 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
-import sys
-
-fstypes = {}
-
-def register_fstype(x):
- if x.name in fstypes.keys():
- return
- fstypes[x.name] = x
-
-class FileSystemType(object):
- """A simple representation for a file system that gives a fs name
- and a method for sniffing a file to see if it's of the given fstype."""
- def __init__(self):
- self.name = ""
-
- def sniff_magic(self, fn, offset = 0):
- """Look at the filesystem at fn for the appropriate magic starting at
- offset offset."""
- raise RuntimeError, "sniff_magic not implemented"
-
- def open_fs(self, fn, offset = 0):
- """Open the given filesystem and return a filesystem object."""
- raise RuntimeError, "open_fs not implemented"
-
-class FileSystem(object):
- def open(self, name, flags = 0, block_size = 0):
- """Open the fsys on name with given flags and block_size."""
- raise RuntimeError, "open not implemented"
-
- def close(self):
- """Close the fsys."""
- raise RuntimeError, "close not implemented"
-
- def open_file(self, file, flags = None):
- """Open the file 'name' with the given flags. The returned object
- should look similar to a native file object."""
- raise RuntimeError, "open_file not implemented"
-
- def file_exist(self, file):
- """Check to see if the give file is existed.
- Return true if file existed, return false otherwise."""
- raise RuntimeError, "file_exist not implemented"
-
-mydir = sys.modules['grub.fsys'].__path__[0]
-for f in os.listdir(mydir):
- if not os.path.isdir("%s/%s" %(mydir, f)):
- continue
- try:
- exec "import grub.fsys.%s" %(f,)
- except ImportError, e:
- pass
diff --git a/tools/pygrub/src/fsys/ext2/__init__.py b/tools/pygrub/src/fsys/ext2/__init__.py
deleted file mode 100644
index ebb5393bcc..0000000000
--- a/tools/pygrub/src/fsys/ext2/__init__.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright 2005 Red Hat, Inc.
-# Jeremy Katz <katzj@redhat.com>
-#
-# 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.
-#
-
-from grub.fsys import register_fstype, FileSystemType
-from _pyext2 import *
-
-import os, struct
-
-class Ext2FileSystemType(FileSystemType):
- def __init__(self):
- FileSystemType.__init__(self)
- self.name = "ext2"
-
- def sniff_magic(self, fn, offset = 0):
- fd = os.open(fn, os.O_RDONLY)
- os.lseek(fd, offset, 0)
- buf = os.read(fd, 2048)
- os.close(fd)
- if len(buf) > 1082 and \
- struct.unpack("<H", buf[1080:1082]) == (0xef53,):
- return True
- return False
-
- def open_fs(self, fn, offset = 0):
- if not self.sniff_magic(fn, offset):
- raise ValueError, "Not an ext2 filesystem"
- return Ext2Fs(fn, offset = offset)
-
-register_fstype(Ext2FileSystemType())
-
diff --git a/tools/pygrub/src/fsys/ext2/ext2module.c b/tools/pygrub/src/fsys/ext2/ext2module.c
deleted file mode 100644
index 57f1a83eaf..0000000000
--- a/tools/pygrub/src/fsys/ext2/ext2module.c
+++ /dev/null
@@ -1,387 +0,0 @@
-/*
- * ext2module.c - simple python binding for libext2fs
- *
- * Copyright 2005 Red Hat, Inc.
- * Jeremy Katz <katzj@redhat.com>
- *
- * 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.
- */
-
-#include <Python.h>
-
-#include <ext2fs/ext2fs.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#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
-
-
-/* global error object */
-PyObject *Ext2Error;
-
-typedef struct _Ext2Fs Ext2Fs;
-struct _Ext2Fs {
- PyObject_HEAD;
- ext2_filsys fs;
-};
-
-typedef struct _Ext2File Ext2File;
-struct _Ext2File {
- PyObject_HEAD;
- ext2_file_t file;
-};
-
-/* ext2 file object */
-
-static PyObject *
-ext2_file_close (Ext2File *file, PyObject *args)
-{
- if (file->file != NULL)
- ext2fs_file_close(file->file);
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-static PyObject *
-ext2_file_read (Ext2File *file, PyObject *args)
-{
- int err, size = 0;
- unsigned int n, total = 0;
- PyObject * buffer = NULL;
-
- if (file->file == NULL) {
- PyErr_SetString(PyExc_ValueError, "Cannot read from closed file");
- return NULL;
- }
-
- if (!PyArg_ParseTuple(args, "|i", &size))
- return NULL;
-
- buffer = PyString_FromStringAndSize((char *) NULL, (size) ? size : 4096);
- if (buffer == NULL)
- return buffer;
-
- while (1) {
- err = ext2fs_file_read(file->file, PyString_AS_STRING(buffer) + total,
- (size) ? size : 4096, &n);
- if (err) {
- if (buffer != NULL) { Py_DECREF(buffer); }
- Py_DECREF(buffer);
- PyErr_SetString(PyExc_ValueError, "read error");
- return NULL;
- }
-
- total += n;
- if (n == 0)
- break;
-
- if (size && size == total)
- break;
-
- if (!size) {
- _PyString_Resize(&buffer, total + 4096);
- }
- }
-
- _PyString_Resize(&buffer, total);
- return buffer;
-}
-
-static void
-ext2_file_dealloc (Ext2File * file)
-{
- if (file->file != NULL)
- ext2fs_file_close(file->file);
- PyMem_DEL(file);
-}
-
-static struct PyMethodDef Ext2FileMethods[] = {
- { "close",
- (PyCFunction) ext2_file_close,
- METH_VARARGS, NULL },
- { "read",
- (PyCFunction) ext2_file_read,
- METH_VARARGS, NULL },
- { NULL, NULL, 0, NULL }
-};
-
-static PyObject *
-ext2_file_getattr (Ext2File * file, char * name)
-{
- return Py_FindMethod (Ext2FileMethods, (PyObject *) file, name);
-}
-
-static char Ext2FileType__doc__[] = "This is the ext2 filesystem object";
-PyTypeObject Ext2FileType = {
- PyObject_HEAD_INIT(&PyType_Type)
- 0, /* ob_size */
- "Ext2File", /* tp_name */
- sizeof(Ext2File), /* tp_size */
- 0, /* tp_itemsize */
- (destructor) ext2_file_dealloc, /* tp_dealloc */
- 0, /* tp_print */
- (getattrfunc) ext2_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 */
- 0L, /* tp_flags */
- Ext2FileType__doc__,
- PY_PAD
-};
-
-static PyObject *
-ext2_file_open (Ext2Fs *fs, char * name, int flags)
-{
- int err;
- ext2_file_t f;
- ext2_ino_t ino;
- Ext2File * file;
-
- file = (Ext2File *) PyObject_NEW(Ext2File, &Ext2FileType);
- file->file = NULL;
-
- err = ext2fs_namei_follow(fs->fs, EXT2_ROOT_INO, EXT2_ROOT_INO, name, &ino);
- if (err) {
- PyErr_SetString(PyExc_ValueError, "unable to open file");
- return NULL;
- }
-
- err = ext2fs_file_open(fs->fs, ino, flags, &f);
- if (err) {
- PyErr_SetString(PyExc_ValueError, "unable to open file");
- return NULL;
- }
-
- file->file = f;
- return (PyObject *) file;
-}
-
-static PyObject *
-ext2_file_exist (Ext2Fs *fs, char * name)
-{
- int err;
- ext2_ino_t ino;
- Ext2File * file;
-
- file = (Ext2File *) PyObject_NEW(Ext2File, &Ext2FileType);
- file->file = NULL;
-
- err = ext2fs_namei_follow(fs->fs, EXT2_ROOT_INO, EXT2_ROOT_INO, name, &ino);
- if (err) {
- Py_INCREF(Py_False);
- return Py_False;
- }
- Py_INCREF(Py_True);
- return Py_True;
-}
-
-/* ext2fs object */
-
-static PyObject *
-ext2_fs_close (Ext2Fs *fs, PyObject *args)
-{
- if (fs->fs != NULL)
- ext2fs_close(fs->fs);
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-static PyObject *
-ext2_fs_open (Ext2Fs *fs, PyObject *args, PyObject *kwargs)
-{
- static char *kwlist[] = { "name", "flags", "superblock",
- "block_size", "offset", NULL };
- char * name;
- int flags = 0, superblock = 0, offset = 0, err;
- unsigned int block_size = 0;
- ext2_filsys efs;
-#ifdef HAVE_EXT2FS_OPEN2
- char offsetopt[30];
-#endif
-
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|iiii", kwlist,
- &name, &flags, &superblock,
- &block_size, &offset))
- return NULL;
-
- if (fs->fs != NULL) {
- PyErr_SetString(PyExc_ValueError, "already have an fs object");
- return NULL;
- }
-
-#ifdef HAVE_EXT2FS_OPEN2
- if (offset == 0) {
- offsetopt[0] = '\0';
- }
- else {
- snprintf(offsetopt, 29, "offset=%d", offset);
- }
-
- err = ext2fs_open2(name, offsetopt, flags, superblock, block_size,
- unix_io_manager, &efs);
-#else
- if (offset != 0) {
- PyErr_SetString(PyExc_ValueError, "offset argument not supported");
- return NULL;
- }
-
- err = ext2fs_open(name, flags, superblock, block_size,
- unix_io_manager, &efs);
-#endif
- if (err) {
- PyErr_SetString(PyExc_ValueError, "unable to open filesystem");
- return NULL;
- }
-
- fs->fs = efs;
-
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-static PyObject *
-ext2_fs_open_file (Ext2Fs *fs, PyObject *args, PyObject *kwargs)
-{
- static char *kwlist[] = { "name", "flags", NULL };
- char * name;
- int flags = 0;
-
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|i", kwlist,
- &name, &flags))
- return NULL;
-
- return ext2_file_open(fs, name, flags);
-}
-
-static PyObject *
-ext2_fs_file_exist (Ext2Fs *fs, PyObject *args, PyObject *kwargs)
-{
- static char *kwlist[] = { "name", NULL };
- char * name;
-
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &name))
- return NULL;
-
- return ext2_file_exist(fs, name);
-}
-
-static void
-ext2_fs_dealloc (Ext2Fs * fs)
-{
- if (fs->fs != NULL)
- ext2fs_close(fs->fs);
- PyMem_DEL(fs);
-}
-
-static struct PyMethodDef Ext2FsMethods[] = {
- { "close",
- (PyCFunction) ext2_fs_close,
- METH_VARARGS, NULL },
- { "open",
- (PyCFunction) ext2_fs_open,
- METH_VARARGS|METH_KEYWORDS, NULL },
- { "open_file",
- (PyCFunction) ext2_fs_open_file,
- METH_VARARGS|METH_KEYWORDS, NULL },
- { "file_exist",
- (PyCFunction) ext2_fs_file_exist,
- METH_VARARGS|METH_KEYWORDS, NULL },
- { NULL, NULL, 0, NULL }
-};
-
-static PyObject *
-ext2_fs_getattr (Ext2Fs * fs, char * name)
-{
- return Py_FindMethod (Ext2FsMethods, (PyObject *) fs, name);
-}
-
-static char Ext2FsType__doc__[] = "This is the ext2 filesystem object";
-PyTypeObject Ext2FsType = {
- PyObject_HEAD_INIT(&PyType_Type)
- 0, /* ob_size */
- "Ext2Fs", /* tp_name */
- sizeof(Ext2Fs), /* tp_size */
- 0, /* tp_itemsize */
- (destructor) ext2_fs_dealloc, /* tp_dealloc */
- 0, /* tp_print */
- (getattrfunc) ext2_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 */
- 0L, /* tp_flags */
- Ext2FsType__doc__,
- PY_PAD
-};
-
-static PyObject *
-ext2_fs_new(PyObject *o, PyObject *args, PyObject *kwargs)
-{
- static char *kwlist[] = { "name", "flags", "superblock",
- "block_size", "offset", NULL };
- char * name;
- int flags = 0, superblock = 0, offset;
- unsigned int block_size = 0;
- Ext2Fs *pfs;
-
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|iiii", kwlist,
- &name, &flags, &superblock, &block_size,
- &offset))
- return NULL;
-
- pfs = (Ext2Fs *) PyObject_NEW(Ext2Fs, &Ext2FsType);
- if (pfs == NULL)
- return NULL;
- pfs->fs = NULL;
-
- if (!ext2_fs_open(pfs,
- Py_BuildValue("siiii", name, flags, superblock,
- block_size, offset), NULL))
- return NULL;
-
- return (PyObject *)pfs;
-}
-
-static struct PyMethodDef Ext2ModuleMethods[] = {
- { "Ext2Fs", (PyCFunction) ext2_fs_new, METH_VARARGS|METH_KEYWORDS, NULL },
- { NULL, NULL, 0, NULL }
-};
-
-void init_pyext2(void) {
- PyObject *m;
-
- m = Py_InitModule("_pyext2", Ext2ModuleMethods);
- /*
- * PyObject *d;
- * d = PyModule_GetDict(m);
- * o = PyObject_NEW(PyObject, yExt2FsConstructorType);
- * PyDict_SetItemString(d, "PyExt2Fs", o);
- * Py_DECREF(o);
- */
-}
diff --git a/tools/pygrub/src/fsys/ext2/test.py b/tools/pygrub/src/fsys/ext2/test.py
deleted file mode 100644
index eeb79506ee..0000000000
--- a/tools/pygrub/src/fsys/ext2/test.py
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/python
-
-
-import _pyext2
-import struct, os, sys
-
-fs = _pyext2.Ext2Fs("test.img")
-
-f = fs.open_file("/boot/vmlinuz-2.6.11-1.1177_FC4")
-buf = f.read()
-o = open("vmlinuz", "wb+")
-o.write(buf)
-o.close()
-
-f.close()
diff --git a/tools/pygrub/src/fsys/reiser/__init__.py b/tools/pygrub/src/fsys/reiser/__init__.py
deleted file mode 100644
index 57551283c0..0000000000
--- a/tools/pygrub/src/fsys/reiser/__init__.py
+++ /dev/null
@@ -1,40 +0,0 @@
-#
-# Copyright (C) 2005 Nguyen Anh Quynh <aquynh@gmail.com>
-#
-# 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.
-#
-
-from grub.fsys import register_fstype, FileSystemType
-from _pyreiser import *
-
-import os
-
-FSMAGIC2 = 'ReIsEr2'
-FSMAGIC3 = 'ReIsEr3'
-
-class ReiserFileSystemType(FileSystemType):
- def __init__(self):
- FileSystemType.__init__(self)
- self.name = "reiser"
-
- def sniff_magic(self, fn, offset = 0):
- fd = os.open(fn, os.O_RDONLY)
- os.lseek(fd, 0x10000, 0)
- buf = os.read(fd, 0x40)
- os.close(fd)
- if len(buf) == 0x40 and (buf[0x34:0x3B] in [FSMAGIC2, FSMAGIC3]) :
- return True
- return False
-
- def open_fs(self, fn, offset = 0):
- if not self.sniff_magic(fn, offset):
- raise ValueError, "Not a reiserfs filesystem"
- return ReiserFs(fn)
-
-register_fstype(ReiserFileSystemType())
-
diff --git a/tools/pygrub/src/fsys/reiser/reisermodule.c b/tools/pygrub/src/fsys/reiser/reisermodule.c
deleted file mode 100644
index 9eb4e1e638..0000000000
--- a/tools/pygrub/src/fsys/reiser/reisermodule.c
+++ /dev/null
@@ -1,345 +0,0 @@
-/*
- * reisermodule.c - simple python binding for libreiserfs{2,3}
- *
- * Copyright (C) 2005 Nguyen Anh Quynh <aquynh@gmail.com>
- *
- * 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.
- */
-
-#include <Python.h>
-
-#include <fcntl.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#include <dal/file_dal.h>
-#include <reiserfs/reiserfs.h>
-
-#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
-
-
-/* global error object */
-PyObject *ReiserError;
-
-typedef struct {
- PyObject_HEAD
- reiserfs_fs_t *fs;
- dal_t *dal;
-} ReiserFs;
-
-typedef struct _ReiserFile ReiserFile;
-struct _ReiserFile {
- PyObject_HEAD
- reiserfs_file_t *file;
-};
-
-void file_dal_close(dal_t *dal) {
-
- if (!dal) return;
-
- close((int)(unsigned long)dal->dev);
- dal_free(dal);
-}
-
-/* reiser file object */
-
-static PyObject *
-reiser_file_close (ReiserFile *file, PyObject *args)
-{
- if (file->file != NULL)
- {
- reiserfs_file_close(file->file);
- file->file = NULL;
- }
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-static PyObject *
-reiser_file_read (ReiserFile *file, PyObject *args)
-{
- int size = 0;
- size_t n, total = 0;
- PyObject * buffer = NULL;
-
- if (file->file == NULL) {
- PyErr_SetString(PyExc_ValueError, "Cannot read from closed file");
- return NULL;
- }
-
- if (!PyArg_ParseTuple(args, "|i", &size))
- return NULL;
-
- buffer = PyString_FromStringAndSize((char *) NULL, (size) ? size : 4096);
- if (buffer == NULL)
- return buffer;
-
- while (1) {
- n = reiserfs_file_read(file->file, PyString_AS_STRING(buffer) + total,
- (size) ? size : 4096);
- if (n == 0)
- break;
-
- total += n;
-
- if (size && size == total)
- break;
-
- if (!size) {
- _PyString_Resize(&buffer, total + 4096);
- }
- }
-
- _PyString_Resize(&buffer, total);
- return buffer;
-}
-
-static void
-reiser_file_dealloc (ReiserFile * file)
-{
- if (file->file != NULL) {
- reiserfs_file_close(file->file);
- file->file = NULL;
- }
- PyObject_DEL(file);
-}
-
-static struct PyMethodDef ReiserFileMethods[] = {
- { "close", (PyCFunction) reiser_file_close, METH_VARARGS, NULL },
- { "read", (PyCFunction) reiser_file_read, METH_VARARGS, NULL },
- { NULL, NULL, 0, NULL }
-};
-
-static PyObject *
-reiser_file_getattr (ReiserFile * file, char * name)
-{
- return Py_FindMethod (ReiserFileMethods, (PyObject *) file, name);
-}
-
-static char ReiserFileType__doc__[] = "This is the reiser filesystem object";
-PyTypeObject ReiserFileType = {
- PyObject_HEAD_INIT(&PyType_Type)
- 0, /* ob_size */
- "ReiserFile", /* tp_name */
- sizeof(ReiserFile), /* tp_size */
- 0, /* tp_itemsize */
- (destructor) reiser_file_dealloc, /* tp_dealloc */
- 0, /* tp_print */
- (getattrfunc) reiser_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 */
- ReiserFileType__doc__,
- PY_PAD
-};
-
-static PyObject *
-reiser_file_open (ReiserFs *fs, char *name, int flags)
-{
- ReiserFile *file;
- reiserfs_file_t *f;
-
- file = (ReiserFile *) PyObject_NEW(ReiserFile, &ReiserFileType);
-
- f = reiserfs_file_open(fs->fs, name, flags);
- file->file = f;
-
- if (!f) {
- PyErr_SetString(PyExc_ValueError, "unable to open file");
- return NULL;
- }
-
- return (PyObject *) file;
-}
-
-static PyObject *
-reiser_file_exist (ReiserFs *fs, char *name)
-{
- reiserfs_file_t *f;
-
- f = reiserfs_file_open(fs->fs, name, O_RDONLY);
-
- if (!f) {
- Py_INCREF(Py_False);
- return Py_False;
- }
- reiserfs_file_close(f);
- Py_INCREF(Py_True);
- return Py_True;
-}
-
-/* reiserfs object */
-
-static PyObject *
-reiser_fs_close (ReiserFs *fs, PyObject *args)
-{
- if (fs->fs != NULL)
- {
- reiserfs_fs_close(fs->fs);
- file_dal_close(fs->dal);
- fs->fs = NULL;
- }
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-static PyObject *
-reiser_fs_open (ReiserFs *fs, PyObject *args)
-{
- char *name;
- size_t block_size = DEFAULT_BLOCK_SIZE;
- dal_t *dal;
- reiserfs_fs_t *rfs;
-
- if (!PyArg_ParseTuple(args, "s|i", &name, &block_size))
- return NULL;
-
- if (fs->fs != NULL) {
- PyErr_SetString(PyExc_ValueError, "already have an fs object");
- return NULL;
- }
-
- if (!(dal = file_dal_open(name, block_size, O_RDONLY))) {
- PyErr_SetString(PyExc_ValueError, "Couldn't create device abstraction");
- return NULL;
- }
-
- if (!(rfs = reiserfs_fs_open_fast(dal, dal))) {
- file_dal_close(dal);
- PyErr_SetString(PyExc_ValueError, "unable to open file");
- return NULL;
- }
-
- fs->fs = rfs;
- fs->dal = dal;
-
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-static PyObject *
-reiser_fs_open_file (ReiserFs *fs, PyObject *args)
-{
- char *name;
- int flags = 0;
-
- if (!PyArg_ParseTuple(args, "s|i", &name, &flags))
- return NULL;
-
- return reiser_file_open(fs, name, flags);
-}
-
-static PyObject *
-reiser_fs_file_exist (ReiserFs *fs, PyObject *args)
-{
- char * name;
-
- if (!PyArg_ParseTuple(args, "s", &name))
- return NULL;
-
- return reiser_file_exist(fs, name);
-}
-
-static void
-reiser_fs_dealloc (ReiserFs * fs)
-{
- if (fs->fs != NULL)
- {
- reiserfs_fs_close(fs->fs);
- file_dal_close(fs->dal);
- fs->fs = NULL;
- }
- PyObject_DEL(fs);
-}
-
-static struct PyMethodDef ReiserFsMethods[] = {
- { "close", (PyCFunction) reiser_fs_close, METH_VARARGS, NULL },
- { "open", (PyCFunction) reiser_fs_open, METH_VARARGS, NULL },
- { "open_file", (PyCFunction) reiser_fs_open_file, METH_VARARGS, NULL },
- { "file_exist", (PyCFunction) reiser_fs_file_exist, METH_VARARGS, NULL },
- { NULL, NULL, 0, NULL }
-};
-
-static PyObject *
-reiser_fs_getattr (ReiserFs * fs, char * name)
-{
- return Py_FindMethod (ReiserFsMethods, (PyObject *) fs, name);
-}
-
-static char ReiserFsType__doc__[] = "This is the reiser filesystem object";
-
-PyTypeObject ReiserFsType = {
- PyObject_HEAD_INIT(&PyType_Type)
- 0, /* ob_size */
- "ReiserFs", /* tp_name */
- sizeof(ReiserFs), /* tp_size */
- 0, /* tp_itemsize */
- (destructor) reiser_fs_dealloc, /* tp_dealloc */
- 0, /* tp_print */
- (getattrfunc) reiser_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 */
- ReiserFsType__doc__,
- PY_PAD
-};
-
-static PyObject *
-reiser_fs_new(PyObject *o, PyObject *args)
-{
- char *name;
- size_t block_size = DEFAULT_BLOCK_SIZE;
- ReiserFs *pfs;
-
- if (!PyArg_ParseTuple(args, "s|i", &name, &block_size))
- return NULL;
-
- pfs = (ReiserFs *) PyObject_NEW(ReiserFs, &ReiserFsType);
- if (pfs == NULL)
- return NULL;
-
- pfs->fs = NULL;
-
- if (!reiser_fs_open(pfs, Py_BuildValue("si", name, block_size)))
- return NULL;
-
- return (PyObject *)pfs;
-}
-
-static struct PyMethodDef ReiserModuleMethods[] = {
- { "ReiserFs", (PyCFunction) reiser_fs_new, METH_VARARGS},
- { NULL, NULL, 0}
-};
-
-void init_pyreiser(void) {
- Py_InitModule("_pyreiser", ReiserModuleMethods);
-}
diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub
index 04982fd4e8..64a3662704 100644
--- a/tools/pygrub/src/pygrub
+++ b/tools/pygrub/src/pygrub
@@ -22,8 +22,8 @@ import getopt
sys.path = [ '/usr/lib/python' ] + sys.path
+import fsimage
import grub.GrubConf
-import grub.fsys
PYGRUB_VER = 0.5
@@ -113,17 +113,21 @@ class GrubLineEditor(curses.textpad.Textbox):
elif ch == curses.ascii.SOH: # ^a
self.pos = 0
elif ch in (curses.ascii.STX,curses.KEY_LEFT):
- self.pos -= 1
+ if self.pos > 0:
+ self.pos -= 1
elif ch in (curses.ascii.BS,curses.KEY_BACKSPACE):
if self.pos > 0:
self.pos -= 1
- self.line.pop(self.pos)
+ if self.pos < len(self.line):
+ self.line.pop(self.pos)
elif ch == curses.ascii.EOT: # ^d
- self.line.pop(self.pos)
+ 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):
- self.pos +=1
+ if self.pos < len(self.line):
+ self.pos +=1
elif ch == curses.ascii.VT: # ^k
self.line = self.line[:self.pos]
else:
@@ -313,25 +317,21 @@ class Grub:
raise RuntimeError, "Unable to find active partition on disk"
# open the image and read the grub config
- fs = None
- for fstype in grub.fsys.fstypes.values():
- if fstype.sniff_magic(fn, offset):
- fs = fstype.open_fs(fn, offset)
- break
+ fs = fsimage.open(fn, offset)
if fs is not None:
grubfile = None
for f in ("/boot/grub/menu.lst", "/boot/grub/grub.conf",
"/grub/menu.lst", "/grub/grub.conf"):
- if fs.file_exist(f):
+ if fs.file_exists(f):
grubfile = f
break
if grubfile is None:
raise RuntimeError, "we couldn't find grub config file in the image provided."
f = fs.open_file(grubfile)
buf = f.read()
- f.close()
- fs.close()
+ del f
+ del fs
# then parse the grub config
self.cf.parse(buf)
else:
@@ -511,14 +511,7 @@ if __name__ == "__main__":
raise RuntimeError, "Unable to find active partition on disk"
# read the kernel and initrd onto the hostfs
- fs = None
- for fstype in grub.fsys.fstypes.values():
- if fstype.sniff_magic(file, offset):
- fs = fstype.open_fs(file, offset)
- break
-
- if fs is None:
- raise RuntimeError, "Unable to open filesystem"
+ fs = fsimage.open(file, offset)
kernel = fs.open_file(img.kernel[1],).read()
(tfd, fn) = tempfile.mkstemp(prefix="vmlinuz.", dir="/var/lib/xen")
diff --git a/tools/python/README.XendConfig b/tools/python/README.XendConfig
new file mode 100644
index 0000000000..d860aac9c7
--- /dev/null
+++ b/tools/python/README.XendConfig
@@ -0,0 +1,159 @@
+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.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.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 0000000000..9f7b787789
--- /dev/null
+++ b/tools/python/README.sxpcfg
@@ -0,0 +1,116 @@
+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
+ (parseDeviceModel)
+ - boot
+ - fda
+ - fdb
+ - soundhw
+ - localtime
+ - 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/scripts/README b/tools/python/scripts/README
new file mode 100644
index 0000000000..a5d87592ae
--- /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 <DomainName> xapi.vbdcfg.py
+xapi.py vif-create <DomainName> 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 0000000000..1e24cc03df
--- /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/xapi.domcfg.py b/tools/python/scripts/xapi.domcfg.py
new file mode 100644
index 0000000000..8565bd8975
--- /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 = ''
+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 0000000000..387ad0a487
--- /dev/null
+++ b/tools/python/scripts/xapi.py
@@ -0,0 +1,537 @@
+#!/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
+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
+
+
+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 '\
+ '%(sector_size)-8s'
+
+COMMANDS = {
+ 'host-info': ('', 'Get Xen Host Info'),
+ 'host-set-name': ('', 'Set host name'),
+ 'sr-list': ('', 'List all SRs'),
+ 'vbd-create': ('<domname> <pycfg> [opts]',
+ 'Create VBD attached to domname'),
+ 'vdi-create': ('<pycfg> [opts]', 'Create a VDI'),
+ 'vdi-list' : ('', 'List all VDI'),
+ 'vdi-rename': ('<vdi_uuid> <new_name>', 'Rename VDI'),
+ 'vdi-delete': ('<vdi_uuid>', 'Delete VDI'),
+ 'vif-create': ('<domname> <pycfg>', 'Create VIF attached to domname'),
+ 'vtpm-create' : ('<domname> <pycfg>', 'Create VTPM attached to domname'),
+
+ 'vm-create': ('<pycfg>', 'Create VM with python config'),
+ 'vm-destroy': ('<domname>', 'Delete VM'),
+
+ 'vm-list': ('[--long]', 'List all domains.'),
+ 'vm-name': ('<uuid>', 'Name of UUID.'),
+ 'vm-shutdown': ('<name> [opts]', 'Shutdown VM with name'),
+ 'vm-start': ('<name>', 'Start VM with name'),
+ 'vm-uuid': ('<name>', 'UUID of a domain by name.'),
+}
+
+OPTIONS = {
+ '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'}),
+ (('--description',), {'help': 'Description for VDI'}),
+ (('--sector-size',), {'type': 'int',
+ 'help': 'Sector size'}),
+ (('--virtual-size',), {'type': 'int',
+ 'help': 'Size of VDI in sectors'}),
+ (('--type',), {'choices': ['system', 'user', 'ephemeral'],
+ 'help': 'VDI type'}),
+ (('--sharable',), {'action': 'store_true',
+ 'help': 'VDI sharable'}),
+ (('--read-only',), {'action': 'store_true',
+ 'help': 'Read only'})],
+
+ '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'}),
+ (('--image',), {'help': 'Location of drive image.'})]
+
+}
+
+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(fn, *args):
+ result = fn(*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:
+ _server = ServerProxy('httpu:///var/run/xend/xmlrpc.sock')
+ login = raw_input("Login: ")
+ password = getpass()
+ creds = (login, password)
+ _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]
+
+#
+# Actual commands
+#
+
+def xapi_host_info(*args):
+ 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)
+
+def xapi_host_set_name(*args):
+ 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):
+ 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):
+ 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):
+ opts, args = parse_args('vm-list', args, set_defaults = True)
+ is_long = opts and opts.long
+
+ 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)
+ 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):
+ 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)
+ print 'Done. (%s)' % uuid
+ print uuid
+
+def xapi_vm_destroy(*args):
+ 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)
+ print 'Done.'
+
+
+def xapi_vm_start(*args):
+ 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)
+ print 'Done.'
+
+def xapi_vm_shutdown(*args):
+ 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)
+ else:
+ print 'Shutting down VM %s (%s)' % (args[0], vm_uuid)
+ success = execute(server.VM.clean_shutdown, session, vm_uuid)
+ print 'Done.'
+
+def xapi_vbd_create(*args):
+ 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)
+ print 'Done. (%s)' % vbd_uuid
+
+def xapi_vif_create(*args):
+ 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)
+ print 'Done. (%s)' % vif_uuid
+
+def xapi_vdi_list(*args):
+ server, session = _connect()
+ vdis = execute(server.VDI.get_all, session)
+
+ print VDI_LIST_FORMAT % {'name_label': 'VDI Label',
+ 'uuid' : 'UUID',
+ 'virtual_size': 'Sectors',
+ 'sector_size': 'Sector Size'}
+
+ for vdi in vdis:
+ vdi_struct = execute(server.VDI.get_record, session, vdi)
+ print VDI_LIST_FORMAT % vdi_struct
+
+def xapi_sr_list(*args):
+ server, session = _connect()
+ srs = execute(server.SR.get_all, session)
+ print SR_LIST_FORMAT % {'name_label': 'SR Label',
+ 'uuid' : 'UUID',
+ 'physical_size': 'Size',
+ '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
+
+def xapi_vdi_create(*args):
+ 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 = execute(server.SR.get_all, session)
+ sr = srs[0]
+ cfg['SR'] = sr
+
+ size = (cfg['virtual_size'] * cfg['sector_size'])/MB
+ print 'Creating VDI of size: %dMB ..' % size,
+ uuid = execute(server.VDI.create, session, cfg)
+ print 'Done. (%s)' % uuid
+
+def xapi_vdi_delete(*args):
+ 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)
+ print 'Done.'
+
+def xapi_vdi_rename(*args):
+ server, session = _connect()
+ if len(args) < 2:
+ raise OptionError('Not enough arguments')
+
+ vdi_uuid = args[0]
+ vdi_name = args[1]
+ print 'Renaming VDI %s to %s' % (vdi_uuid, vdi_name)
+ result = execute(server.VDI.set_name_label, session, vdi_uuid, vdi_name)
+ print 'Done.'
+
+
+def xapi_vtpm_create(*args):
+ 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
+ vtpm_id = execute(server.VTPM.get_instance, session, vtpm_uuid)
+ print "Has instance number '%s'" % vtpm_id
+ vtpm_be = execute(server.VTPM.get_backend, session, vtpm_uuid)
+ print "Has backend in '%s'" % vtpm_be
+ driver = execute(server.VTPM.get_driver, session, vtpm_uuid)
+ print "Has driver type '%s'" % driver
+ vtpm_rec = execute(server.VTPM.get_record, session, vtpm_uuid)
+ print "Has vtpm record '%s'" % vtpm_rec
+ vm = execute(server.VTPM.get_VM, session, vtpm_uuid)
+ print "Has VM '%s'" % vm
+
+
+#
+# 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('-', '_')
+ func_name = 'xapi_%s' % cmd_name
+ func = globals().get(func_name)
+ if func:
+ try:
+ args = tuple(words[1:])
+ func(*args)
+ 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 <subcommand> [options] [args]'
+ print
+ print 'Subcommands:'
+ print
+ sorted_commands = sorted(COMMANDS.keys())
+ for command in sorted_commands:
+ args, description = COMMANDS[command]
+ print '%-16s %-40s' % (command, description)
+ print
+ else:
+ parse_args(command, ['-h'])
+
+def main(args):
+
+ if len(args) < 1 or args[0] in ('-h', '--help', 'help'):
+ usage()
+ sys.exit(1)
+
+ subcmd = args[0]
+ subcmd_func_name = 'xapi_' + subcmd.replace('-', '_')
+ 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)
+
+ if '-h' in args[1:] or '--help' in args[1:]:
+ usage(subcmd)
+ sys.exit(1)
+
+ try:
+ subcmd_func(*args[1:])
+ except XenAPIError, e:
+ print 'Error: %s' % str(e.args[1])
+ 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 0000000000..82dcaf8ba7
--- /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 0000000000..f694b83cf5
--- /dev/null
+++ b/tools/python/scripts/xapi.vdicfg.py
@@ -0,0 +1,7 @@
+name_label = 'VDI 1'
+name_description = ''
+virtual_size = 10 * 1024
+sector_size = 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 0000000000..f42122409b
--- /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 0000000000..7c419baa26
--- /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
index 640dcef000..56dd3e4a0b 100644
--- a/tools/python/setup.py
+++ b/tools/python/setup.py
@@ -4,8 +4,7 @@ import os
XEN_ROOT = "../.."
-extra_compile_args = [ "-fno-strict-aliasing", "-Wall", "-Werror" ]
-
+extra_compile_args = [ "-fno-strict-aliasing", "-Werror" ]
include_dirs = [ XEN_ROOT + "/tools/libxc",
XEN_ROOT + "/tools/xenstore",
diff --git a/tools/python/xen/lowlevel/xc/xc.c b/tools/python/xen/lowlevel/xc/xc.c
index 3137623505..51831838f7 100644
--- a/tools/python/xen/lowlevel/xc/xc.c
+++ b/tools/python/xen/lowlevel/xc/xc.c
@@ -65,18 +65,17 @@ static PyObject *pyxc_domain_create(XcObject *self,
PyObject *args,
PyObject *kwds)
{
- uint32_t dom = 0;
- int ret, i;
- uint32_t ssidref = 0;
+ uint32_t dom = 0, ssidref = 0, flags = 0;
+ int ret, i, hvm = 0;
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[] = { "dom", "ssidref", "handle", NULL };
+ static char *kwd_list[] = { "domid", "ssidref", "handle", "hvm", NULL };
- if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|iiO", kwd_list,
- &dom, &ssidref, &pyhandle))
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|iiOi", kwd_list,
+ &dom, &ssidref, &pyhandle, &hvm))
return NULL;
if ( pyhandle != NULL )
@@ -94,7 +93,11 @@ static PyObject *pyxc_domain_create(XcObject *self,
}
}
- if ( (ret = xc_domain_create(self->xc_handle, ssidref, handle, &dom)) < 0 )
+ if ( hvm )
+ flags |= XEN_DOMCTL_CDF_hvm_guest;
+
+ if ( (ret = xc_domain_create(self->xc_handle, ssidref,
+ handle, flags, &dom)) < 0 )
return PyErr_SetFromErrno(xc_error);
return PyInt_FromLong(dom);
@@ -144,7 +147,7 @@ static PyObject *pyxc_vcpu_setaffinity(XcObject *self,
uint64_t cpumap = ~0ULL;
PyObject *cpulist = NULL;
- static char *kwd_list[] = { "dom", "vcpu", "cpumap", NULL };
+ static char *kwd_list[] = { "domid", "vcpu", "cpumap", NULL };
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i|iO", kwd_list,
&dom, &vcpu, &cpulist) )
@@ -171,7 +174,7 @@ static PyObject *pyxc_domain_setcpuweight(XcObject *self,
uint32_t dom;
float cpuweight = 1;
- static char *kwd_list[] = { "dom", "cpuweight", NULL };
+ static char *kwd_list[] = { "domid", "cpuweight", NULL };
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i|f", kwd_list,
&dom, &cpuweight) )
@@ -254,11 +257,12 @@ static PyObject *pyxc_domain_getinfo(XcObject *self,
PyObject *pyhandle = PyList_New(sizeof(xen_domain_handle_t));
for ( j = 0; j < sizeof(xen_domain_handle_t); j++ )
PyList_SetItem(pyhandle, j, PyInt_FromLong(info[i].handle[j]));
- info_dict = Py_BuildValue("{s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s: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}",
- "dom", info[i].domid,
+ "domid", 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,
@@ -291,7 +295,7 @@ static PyObject *pyxc_vcpu_getinfo(XcObject *self,
int rc, i;
uint64_t cpumap;
- static char *kwd_list[] = { "dom", "vcpu", NULL };
+ static char *kwd_list[] = { "domid", "vcpu", NULL };
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i|i", kwd_list,
&dom, &vcpu) )
@@ -331,24 +335,25 @@ static PyObject *pyxc_linux_build(XcObject *self,
char *image, *ramdisk = NULL, *cmdline = "", *features = NULL;
int flags = 0;
int store_evtchn, console_evtchn;
+ unsigned int mem_mb;
unsigned long store_mfn = 0;
unsigned long console_mfn = 0;
- static char *kwd_list[] = { "dom", "store_evtchn",
+ static char *kwd_list[] = { "domid", "store_evtchn", "memsize",
"console_evtchn", "image",
/* optional */
"ramdisk", "cmdline", "flags",
"features", NULL };
- if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iiis|ssis", kwd_list,
- &dom, &store_evtchn,
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iiiis|ssis", kwd_list,
+ &dom, &store_evtchn, &mem_mb,
&console_evtchn, &image,
/* optional */
&ramdisk, &cmdline, &flags,
&features) )
return NULL;
- if ( xc_linux_build(self->xc_handle, dom, image,
+ if ( xc_linux_build(self->xc_handle, dom, mem_mb, image,
ramdisk, cmdline, features, flags,
store_evtchn, &store_mfn,
console_evtchn, &console_mfn) != 0 ) {
@@ -372,19 +377,18 @@ static PyObject *pyxc_hvm_build(XcObject *self,
int vcpus = 1;
int pae = 0;
int acpi = 0;
- int apic = 0;
unsigned long store_mfn = 0;
- static char *kwd_list[] = { "dom", "store_evtchn", "memsize", "image",
- "vcpus", "pae", "acpi", "apic",
- NULL };
- if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iiisiiii", kwd_list,
+ static char *kwd_list[] = { "domid", "store_evtchn",
+ "memsize", "image", "vcpus", "pae", "acpi",
+ NULL };
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iiisiii", kwd_list,
&dom, &store_evtchn, &memsize,
- &image, &vcpus, &pae, &acpi, &apic) )
+ &image, &vcpus, &pae, &acpi) )
return NULL;
if ( xc_hvm_build(self->xc_handle, dom, memsize, image,
- vcpus, pae, acpi, apic, store_evtchn, &store_mfn) != 0 )
+ vcpus, pae, acpi, store_evtchn, &store_mfn) != 0 )
return PyErr_SetFromErrno(xc_error);
return Py_BuildValue("{s:i}", "store_mfn", store_mfn);
@@ -397,7 +401,7 @@ static PyObject *pyxc_evtchn_alloc_unbound(XcObject *self,
uint32_t dom, remote_dom;
int port;
- static char *kwd_list[] = { "dom", "remote_dom", NULL };
+ static char *kwd_list[] = { "domid", "remote_dom", NULL };
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "ii", kwd_list,
&dom, &remote_dom) )
@@ -416,7 +420,7 @@ static PyObject *pyxc_physdev_pci_access_modify(XcObject *self,
uint32_t dom;
int bus, dev, func, enable, ret;
- static char *kwd_list[] = { "dom", "bus", "dev", "func", "enable", NULL };
+ static char *kwd_list[] = { "domid", "bus", "dev", "func", "enable", NULL };
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iiiii", kwd_list,
&dom, &bus, &dev, &func, &enable) )
@@ -557,7 +561,7 @@ static PyObject *pyxc_sedf_domain_set(XcObject *self,
uint32_t domid;
uint64_t period, slice, latency;
uint16_t extratime, weight;
- static char *kwd_list[] = { "dom", "period", "slice",
+ static char *kwd_list[] = { "domid", "period", "slice",
"latency", "extratime", "weight",NULL };
if( !PyArg_ParseTupleAndKeywords(args, kwds, "iLLLhh", kwd_list,
@@ -586,7 +590,7 @@ static PyObject *pyxc_sedf_domain_get(XcObject *self, PyObject *args)
return PyErr_SetFromErrno(xc_error);
return Py_BuildValue("{s:i,s:L,s:L,s:L,s:i,s:i}",
- "domain", domid,
+ "domid", domid,
"period", period,
"slice", slice,
"latency", latency,
@@ -647,6 +651,15 @@ static PyObject *pyxc_shadow_mem_control(PyObject *self,
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);
+
+ return Py_BuildValue("i", sched_id);
+}
+
static PyObject *pyxc_sched_credit_domain_set(XcObject *self,
PyObject *args,
PyObject *kwds)
@@ -654,7 +667,7 @@ static PyObject *pyxc_sched_credit_domain_set(XcObject *self,
uint32_t domid;
uint16_t weight;
uint16_t cap;
- static char *kwd_list[] = { "dom", "weight", "cap", NULL };
+ static char *kwd_list[] = { "domid", "weight", "cap", NULL };
static char kwd_type[] = "I|HH";
struct xen_domctl_sched_credit sdom;
@@ -714,7 +727,7 @@ static PyObject *pyxc_domain_memory_increase_reservation(XcObject *self,
unsigned int extent_order = 0 , address_bits = 0;
unsigned long nr_extents;
- static char *kwd_list[] = { "dom", "mem_kb", "extent_order", "address_bits", NULL };
+ static char *kwd_list[] = { "domid", "mem_kb", "extent_order", "address_bits", NULL };
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "il|ii", kwd_list,
&dom, &mem_kb, &extent_order, &address_bits) )
@@ -739,7 +752,7 @@ static PyObject *pyxc_domain_ioport_permission(XcObject *self,
uint32_t dom;
int first_port, nr_ports, allow_access, ret;
- static char *kwd_list[] = { "dom", "first_port", "nr_ports", "allow_access", NULL };
+ 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) )
@@ -762,7 +775,7 @@ static PyObject *pyxc_domain_irq_permission(PyObject *self,
uint32_t dom;
int pirq, allow_access, ret;
- static char *kwd_list[] = { "dom", "pirq", "allow_access", NULL };
+ static char *kwd_list[] = { "domid", "pirq", "allow_access", NULL };
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iii", kwd_list,
&dom, &pirq, &allow_access) )
@@ -785,7 +798,7 @@ static PyObject *pyxc_domain_iomem_permission(PyObject *self,
uint32_t dom;
unsigned long first_pfn, nr_pfns, allow_access, ret;
- static char *kwd_list[] = { "dom", "first_pfn", "nr_pfns", "allow_access", NULL };
+ 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) )
@@ -976,6 +989,12 @@ static PyMethodDef pyxc_methods[] = {
" vcpus [int, 1]: Number of Virtual CPUS in domain.\n\n"
"Returns: [int] 0 on success; -1 on error.\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"
@@ -1242,6 +1261,11 @@ PyMODINIT_FUNC initxc(void)
Py_INCREF(xc_error);
PyModule_AddObject(m, "Error", xc_error);
+
+ /* Expose some libxc constants to Python */
+ PyModule_AddIntConstant(m, "XEN_SCHEDULER_SEDF", XEN_SCHEDULER_SEDF);
+ PyModule_AddIntConstant(m, "XEN_SCHEDULER_CREDIT", XEN_SCHEDULER_CREDIT);
+
}
diff --git a/tools/python/xen/util/blkif.py b/tools/python/xen/util/blkif.py
index 8c982b7beb..363bd41dd2 100644
--- a/tools/python/xen/util/blkif.py
+++ b/tools/python/xen/util/blkif.py
@@ -21,11 +21,17 @@ def blkdev_name_to_number(name):
try:
return os.stat(n).st_rdev
except Exception, ex:
- log.debug("exception looking up device number for %s: %s", name, ex)
pass
- if re.match( '/dev/sd[a-p]([1-9]|1[0-5])?', n):
- return 8 * 256 + 16 * (ord(n[7:8]) - ord('a')) + int(n[8:] or 0)
+ 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)
+ return major * 256 + minor
+ if 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)
+ return major * 256 + minor
if 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 ]
@@ -53,7 +59,7 @@ def blkdev_segment(name):
"""
val = None
n = blkdev_name_to_number(name)
- if n:
+ if not n is None:
val = { 'device' : n,
'start_sector' : long(0),
'nr_sectors' : long(1L<<63),
diff --git a/tools/python/xen/util/xmlrpclib2.py b/tools/python/xen/util/xmlrpclib2.py
index b353b1cf00..120ab071bc 100644
--- a/tools/python/xen/util/xmlrpclib2.py
+++ b/tools/python/xen/util/xmlrpclib2.py
@@ -21,11 +21,11 @@ An enhanced XML-RPC client/server interface for Python.
"""
import string
-import types
import fcntl
+from types import *
+
from httplib import HTTPConnection, HTTP
-from xmlrpclib import Transport
from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
import SocketServer
import xmlrpclib, socket, os, stat
@@ -40,6 +40,23 @@ except ImportError:
# package.
ssh_enabled = False
+#
+# Convert all integers to strings as described in the Xen API
+#
+
+
+def stringify(value):
+ if isinstance(value, IntType) and not isinstance(value, BooleanType):
+ return str(value)
+ elif isinstance(value, DictType):
+ for k, v in value.items():
+ value[k] = stringify(v)
+ return value
+ elif isinstance(value, (TupleType, ListType)):
+ return [stringify(v) for v in value]
+ else:
+ return value
+
# A new ServerProxy that also supports httpu urls. An http URL comes in the
# form:
@@ -81,18 +98,18 @@ class HTTPUnixConnection(HTTPConnection):
class HTTPUnix(HTTP):
_connection_class = HTTPUnixConnection
-class UnixTransport(Transport):
+class UnixTransport(xmlrpclib.Transport):
def request(self, host, handler, request_body, verbose=0):
self.__handler = handler
- return Transport.request(self, host, '/RPC2', request_body, verbose)
+ return xmlrpclib.Transport.request(self, host, '/RPC2',
+ request_body, verbose)
def make_connection(self, host):
return HTTPUnix(self.__handler)
# See _marshalled_dispatch below.
def conv_string(x):
- if (isinstance(x, types.StringType) or
- isinstance(x, unicode)):
+ if isinstance(x, StringTypes):
s = string.replace(x, "'", r"\047")
exec "s = '" + s + "'"
return s
@@ -134,7 +151,7 @@ class TCPXMLRPCServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer):
allow_reuse_address = True
def __init__(self, addr, requestHandler=XMLRPCRequestHandler,
- logRequests=1):
+ logRequests = 1):
SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests)
flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
@@ -169,8 +186,7 @@ class TCPXMLRPCServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer):
# to transmit the string using Python encoding.
# Thanks to David Mertz <mertz@gnosis.cx> for the trick (buried
# in xml_pickle.py).
- if (isinstance(response, types.StringType) or
- isinstance(response, unicode)):
+ if isinstance(response, StringTypes):
response = repr(response)[1:-1]
response = (response,)
@@ -201,7 +217,7 @@ class UnixXMLRPCRequestHandler(XMLRPCRequestHandler):
class UnixXMLRPCServer(TCPXMLRPCServer):
address_family = socket.AF_UNIX
- def __init__(self, addr, logRequests):
+ def __init__(self, addr, logRequests = 1):
parent = os.path.dirname(addr)
if os.path.exists(parent):
os.chown(parent, os.geteuid(), os.getegid())
diff --git a/tools/python/xen/xend/Args.py b/tools/python/xen/xend/Args.py
index ad6252810d..e2193b6069 100644
--- a/tools/python/xen/xend/Args.py
+++ b/tools/python/xen/xend/Args.py
@@ -18,7 +18,7 @@
import types
import StringIO
-import sxp
+from xen.xend import sxp
class ArgError(StandardError):
pass
diff --git a/tools/python/xen/xend/PrettyPrint.py b/tools/python/xen/xend/PrettyPrint.py
index 2819f02dcf..48135771ac 100644
--- a/tools/python/xen/xend/PrettyPrint.py
+++ b/tools/python/xen/xend/PrettyPrint.py
@@ -22,7 +22,7 @@
import sys
import types
import StringIO
-import sxp
+from xen.xend import sxp
class PrettyItem:
diff --git a/tools/python/xen/xend/XendAPI.py b/tools/python/xen/xend/XendAPI.py
new file mode 100644
index 0000000000..641c7e5180
--- /dev/null
+++ b/tools/python/xen/xend/XendAPI.py
@@ -0,0 +1,1531 @@
+#============================================================================
+# 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 import XendDomain, XendDomainInfo, XendNode
+from xen.xend import XendLogging
+
+from xen.xend.XendAuthSessions import instance as auth_manager
+from xen.xend.XendAuthSessions import session_required
+from xen.xend.XendError import *
+from xen.xend.XendClient import ERROR_INVALID_DOMAIN
+from xen.xend.XendLogging import log
+
+from xen.xend.XendAPIConstants import *
+from xen.util.xmlrpclib2 import stringify
+
+# ------------------------------------------
+# Utility Methods for Xen API Implementation
+# ------------------------------------------
+
+def xen_api_success(value):
+ """Wraps a return value in XenAPI format."""
+ return {"Status": "Success", "Value": stringify(value)}
+
+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."""
+ return {"Status": "Error", "ErrorDescription": error}
+
+def xen_api_todo():
+ """Temporary method to make sure we track down all the TODOs"""
+ return {"Status": "Error", "ErrorDescription": XEND_ERROR_TODO}
+
+# ---------------------------------------------------
+# 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 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
+ """
+ def check_host_ref(self, session, host_ref, *args, **kwargs):
+ xennode = XendNode.instance()
+ if type(host_ref) == type(str()) and xennode.is_valid_host(host_ref):
+ return func(self, session, host_ref, *args, **kwargs)
+ else:
+ return {'Status': 'Failure',
+ 'ErrorDescription': XEND_ERROR_HOST_INVALID}
+
+ # make sure we keep the 'api' attribute
+ if hasattr(func, 'api'):
+ check_host_ref.api = func.api
+
+ return check_host_ref
+
+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
+ """
+ def check_host_cpu_ref(self, session, host_cpu_ref, *args, **kwargs):
+ xennode = XendNode.instance()
+ if type(host_cpu_ref) == type(str()) and \
+ xennode.is_valid_cpu(host_cpu_ref):
+ return func(self, session, host_cpu_ref, *args, **kwargs)
+ else:
+ return {'Status': 'Failure',
+ 'ErrorDescription': XEND_ERROR_HOST_CPU_INVALID}
+
+ # make sure we keep the 'api' attribute
+ if hasattr(func, 'api'):
+ check_host_cpu_ref.api = func.api
+
+ return check_host_cpu_ref
+
+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
+ """
+ def check_vm_ref(self, session, vm_ref, *args, **kwargs):
+ xendom = XendDomain.instance()
+ if type(vm_ref) == type(str()) and \
+ xendom.is_valid_vm(vm_ref):
+ return func(self, session, vm_ref, *args, **kwargs)
+ else:
+ return {'Status': 'Failure',
+ 'ErrorDescription': XEND_ERROR_VM_INVALID}
+
+ # make sure we keep the 'api' attribute
+ if hasattr(func, 'api'):
+ check_vm_ref.api = func.api
+
+ return check_vm_ref
+
+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
+ """
+ def check_vbd_ref(self, session, vbd_ref, *args, **kwargs):
+ xendom = XendDomain.instance()
+ if type(vbd_ref) == type(str()) and \
+ xendom.is_valid_dev('vbd', vbd_ref):
+ return func(self, session, vbd_ref, *args, **kwargs)
+ else:
+ return {'Status': 'Failure',
+ 'ErrorDescription': XEND_ERROR_VBD_INVALID}
+
+ # make sure we keep the 'api' attribute
+ if hasattr(func, 'api'):
+ check_vbd_ref.api = func.api
+
+ return check_vbd_ref
+
+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
+ """
+ def check_vif_ref(self, session, vif_ref, *args, **kwargs):
+ xendom = XendDomain.instance()
+ if type(vif_ref) == type(str()) and \
+ xendom.is_valid_dev('vif', vif_ref):
+ return func(self, session, vif_ref, *args, **kwargs)
+ else:
+ return {'Status': 'Failure',
+ 'ErrorDescription': XEND_ERROR_VIF_INVALID}
+
+ # make sure we keep the 'api' attribute
+ if hasattr(func, 'api'):
+ check_vif_ref.api = func.api
+
+ return check_vif_ref
+
+
+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
+ """
+ def check_vdi_ref(self, session, vdi_ref, *args, **kwargs):
+ xennode = XendNode.instance()
+ if type(vdi_ref) == type(str()) and \
+ xennode.get_sr().is_valid_vdi(vdi_ref):
+ return func(self, session, vdi_ref, *args, **kwargs)
+ else:
+ return {'Status': 'Failure',
+ 'ErrorDescription': XEND_ERROR_VDI_INVALID}
+
+ # make sure we keep the 'api' attribute
+ if hasattr(func, 'api'):
+ check_vdi_ref.api = func.api
+
+ return check_vdi_ref
+
+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
+ """
+ def check_vtpm_ref(self, session, vtpm_ref, *args, **kwargs):
+ xendom = XendDomain.instance()
+ if type(vtpm_ref) == type(str()) and \
+ xendom.is_valid_dev('vtpm', vtpm_ref):
+ return func(self, session, vtpm_ref, *args, **kwargs)
+ else:
+ return {'Status': 'Failure',
+ 'ErrorDescription': XEND_ERROR_VTPM_INVALID}
+
+ # make sure we keep the 'api' attribute
+ if hasattr(func, 'api'):
+ check_vtpm_ref.api = func.api
+
+ return check_vtpm_ref
+
+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
+ """
+ def check_sr_ref(self, session, sr_ref, *args, **kwargs):
+ xennode = XendNode.instance()
+ if type(sr_ref) == type(str()) and \
+ xennode.get_sr().uuid == sr_ref:
+ return func(self, session, sr_ref, *args, **kwargs)
+ else:
+ return {'Status': 'Failure',
+ 'ErrorDescription': XEND_ERROR_SR_INVALID}
+
+ # make sure we keep the 'api' attribute
+ if hasattr(func, 'api'):
+ check_sr_ref.api = func.api
+
+ return check_sr_ref
+
+# -----------------------------
+# Bridge to Legacy XM API calls
+# -----------------------------
+
+def do_vm_func(fn_name, vm_ref, *args):
+ """Helper wrapper func to abstract away from repeative 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
+ """
+ xendom = XendDomain.instance()
+ fn = getattr(xendom, fn_name)
+ return xen_api_success(xendom.do_legacy_api_with_uuid(
+ fn, vm_ref, *args))
+
+
+class XendAPI:
+ """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{XendAuthManager.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.
+ """
+
+ def __init__(self):
+ """Initialised Xen API wrapper by making sure all functions
+ have the correct validation decorators such as L{valid_host}
+ and L{session_required}.
+ """
+
+ classes = {
+ 'session': (session_required,),
+ 'host': (valid_host, session_required),
+ 'host_cpu': (valid_host_cpu, session_required),
+ 'VM': (valid_vm, session_required),
+ 'VBD': (valid_vbd, session_required),
+ 'VIF': (valid_vif, session_required),
+ 'VDI': (valid_vdi, session_required),
+ 'VTPM':(valid_vtpm, session_required),
+ 'SR': (valid_sr, session_required)}
+
+ # 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 cls in classes.keys():
+ get_by_uuid = '%s_get_by_uuid' % cls.lower()
+ get_uuid = '%s_get_uuid' % cls.lower()
+ setattr(XendAPI, get_by_uuid,
+ lambda s, sess, obj_ref: xen_api_success(obj_ref))
+ setattr(XendAPI, get_uuid,
+ lambda s, sess, obj_ref: xen_api_success(obj_ref))
+
+ # 2. get_record is just getting all the attributes, so provide
+ # a fake template implementation.
+ #
+ # TODO: ...
+
+
+ # Wrapping validators around XMLRPC calls
+ # ---------------------------------------
+
+ for cls, validators in classes.items():
+ ro_attrs = getattr(self, '%s_attr_ro' % cls, [])
+ rw_attrs = getattr(self, '%s_attr_rw' % cls, [])
+ methods = getattr(self, '%s_methods' % cls, [])
+ funcs = getattr(self, '%s_funcs' % cls, [])
+
+ # wrap validators around readable class attributes
+ for attr_name in ro_attrs + rw_attrs + self.Base_attr_ro:
+ getter_name = '%s_get_%s' % (cls.lower(), attr_name.lower())
+ try:
+ getter = getattr(XendAPI, getter_name)
+ for validator in validators:
+ getter = validator(getter)
+ getter.api = '%s.get_%s' % (cls, attr_name)
+ setattr(XendAPI, getter_name, getter)
+ except AttributeError:
+ pass
+ #log.warn("API call: %s not found" % getter_name)
+
+ # wrap validators around writable class attrributes
+ for attr_name in rw_attrs + self.Base_attr_rw:
+ setter_name = '%s_set_%s' % (cls.lower(), attr_name.lower())
+ try:
+ setter = getattr(XendAPI, setter_name)
+ for validator in validators:
+ setter = validator(setter)
+ setter.api = '%s.set_%s' % (cls, attr_name)
+ setattr(XendAPI, setter_name, setter)
+ except AttributeError:
+ pass
+ #log.warn("API call: %s not found" % setter_name)
+
+ # wrap validators around methods
+ for method_name in methods + self.Base_methods:
+ method_full_name = '%s_%s' % (cls.lower(),method_name.lower())
+ try:
+ method = getattr(XendAPI, method_full_name)
+ for validator in validators:
+ method = validator(method)
+ method.api = '%s.%s' % (cls, method_name)
+ setattr(XendAPI, method_full_name, method)
+ except AttributeError:
+ pass
+ #log.warn('API call: %s not found' % method_full_name)
+
+ # wrap validators around class functions
+ for func_name in funcs + self.Base_funcs:
+ func_full_name = '%s_%s' % (cls.lower(), func_name.lower())
+ try:
+ method = getattr(XendAPI, func_full_name)
+ method = session_required(method)
+ method.api = '%s.%s' % (cls, func_name)
+ setattr(XendAPI, func_full_name, method)
+ except AttributeError:
+ pass
+ #log.warn('API call: %s not found' % func_full_name)
+
+
+ Base_attr_ro = ['uuid']
+ Base_attr_rw = []
+ Base_methods = ['destroy', 'to_XML', 'get_record']
+ Base_funcs = ['create', 'get_by_uuid', 'get_all']
+
+ # Xen API: Class Session
+ # ----------------------------------------------------------------
+ # NOTE: Left unwrapped by __init__
+
+ session_attr_ro = ['this_host', 'this_user']
+ session_methods = ['logout']
+ # session_funcs = ['login_with_password']
+
+ def session_login_with_password(self, username, password):
+ try:
+ session = auth_manager().login_with_password(username, password)
+ return xen_api_success(session)
+ except XendError, e:
+ return xen_api_error(XEND_ERROR_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_destroy(self, session):
+ return xen_api_error(XEND_ERROR_UNSUPPORTED)
+ def session_get_record(self, session):
+ record = {'this_host': XendNode.instance().uuid,
+ 'this_user': auth_manager().get_user(session)}
+ return xen_api_success(record)
+ def session_to_xml(self, session):
+ return xen_api_todo()
+
+ # attributes (ro)
+ def session_get_this_host(self, session):
+ return xen_api_success(XendNode.instance().uuid)
+ def session_get_this_user(self, session):
+ user = auth_manager().get_user(session)
+ if user:
+ return xen_api_success(user)
+ return xen_api_error(XEND_ERROR_SESSION_INVALID)
+
+
+ # Xen API: Class User
+ # ----------------------------------------------------------------
+ # TODO: NOT IMPLEMENTED YET
+
+ # Xen API: Class Tasks
+ # ----------------------------------------------------------------
+ # TODO: NOT IMPLEMENTED YET
+
+ # Xen API: Class Host
+ # ----------------------------------------------------------------
+
+ host_attr_ro = ['software_version',
+ 'resident_VMs',
+ 'host_CPUs']
+
+ host_attr_rw = ['name_label',
+ 'name_description']
+
+ host_methods = ['disable',
+ 'enable',
+ 'reboot',
+ 'shutdown']
+
+ host_funcs = ['get_by_name_label']
+
+ # 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().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_software_version(self, session, host_ref):
+ return xen_api_success(XendNode.instance().xen_version())
+ def host_get_resident_VMs(self, session, host_ref):
+ return xen_api_success(XendDomain.instance().get_domain_refs())
+ def host_get_host_CPUs(self, session, host_ref):
+ return xen_api_success(XendNode.instance().get_host_cpu_refs())
+
+ # object methods
+ def host_destroy(self, session, host_ref):
+ return xen_api_error(XEND_ERROR_UNSUPPORTED)
+ 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_get_record(self, session, host_ref):
+ node = XendNode.instance()
+ dom = XendDomain.instance()
+ record = {'name_label': node.name,
+ 'name_description': '',
+ 'software_version': node.xen_version(),
+ 'resident_VMs': dom.get_domain_refs(),
+ 'host_CPUs': node.get_host_cpu_refs()}
+ return xen_api_success(record)
+
+ # class methods
+ def host_get_all(self, session):
+ return xen_api_success((XendNode.instance().uuid,))
+ def host_create(self, session, struct):
+ return xen_api_error(XEND_ERROR_UNSUPPORTED)
+
+ # Xen API: Class Host_CPU
+ # ----------------------------------------------------------------
+
+ host_cpu_attr_ro = ['host',
+ 'number',
+ 'features',
+ 'utilisation']
+
+ # attributes
+ def host_cpu_get_uuid(self, session, host_cpu_ref):
+ uuid = XendNode.instance().get_host_cpu_uuid(host_cpu_ref)
+ return xen_api_success(uuid)
+ def host_cpu_get_host(self, session, host_cpu_ref):
+ return xen_api_success(XendNode.instance().uuid)
+ def host_cpu_get_features(self, session, host_cpu_ref):
+ features = XendNode.instance().get_host_cpu_features(host_cpu_ref)
+ return xen_api_success(features)
+ def host_cpu_get_utilisation(self, session, host_cpu_ref):
+ util = XendNode.instance().get_host_cpu_load(host_cpu_ref)
+ return xen_api_success(util)
+ def host_cpu_get_number(self, session, host_cpu_ref):
+ num = XendNode.instance().get_host_cpu_number(host_cpu_ref)
+ return xen_api_success(num)
+
+ # object methods
+ def host_cpu_destroy(self, session, host_cpu_ref):
+ return xen_api_error(XEND_ERROR_UNSUPPORTED)
+ def host_cpu_get_record(self, session, host_cpu_ref):
+ node = XendNode.instance()
+ record = {'uuid': host_cpu_ref,
+ 'host': node.uuid,
+ 'number': node.get_host_cpu_number(host_cpu_ref),
+ 'features': node.get_host_cpu_features(host_cpu_ref),
+ 'utilisation': node.get_host_cpu_load(host_cpu_ref)}
+ return xen_api_success(record)
+ def host_cpu_to_xml(self, session, host_cpu_ref):
+ return xen_api_todo()
+
+ # class methods
+ def host_cpu_get_all(self, session):
+ return xen_api_success(XendNode.instance().get_host_cpu_refs())
+ def host_cpu_create(self, session, struct):
+ return xen_api_error(XEND_ERROR_UNSUPPORTED)
+
+
+ # Xen API: Class Network
+ # ----------------------------------------------------------------
+ # TODO: NOT IMPLEMENTED
+
+ Network_attr_ro = ['VIFs']
+ Network_attr_rw = ['name_label',
+ 'name_description',
+ 'NIC',
+ 'VLAN',
+ 'default_gateway',
+ 'default_netmask']
+
+ # Xen API: Class VM
+ # ----------------------------------------------------------------
+
+ VM_attr_ro = ['power_state',
+ 'resident_on',
+ 'memory_actual',
+ 'memory_static_max',
+ 'memory_static_min',
+ 'VCPUs_number',
+ 'VCPUs_utilisation',
+ 'VCPUs_features_required',
+ 'VCPUs_can_use',
+ 'VIFs',
+ 'VBDs',
+ 'VTPMs',
+ 'PCI_bus',
+ 'tools_version',
+ ]
+
+ VM_attr_rw = ['name_label',
+ 'name_description',
+ 'user_version',
+ 'is_a_template',
+ 'memory_dynamic_max',
+ 'memory_dynamic_min',
+ 'VCPUs_policy',
+ 'VCPUs_params',
+ 'VCPUs_features_force_on',
+ 'VCPUS_features_force_off',
+ 'actions_after_shutdown',
+ 'actions_after_reboot',
+ 'actions_after_suspend',
+ 'actions_after_crash',
+ 'bios_boot',
+ 'platform_std_VGA',
+ 'platform_serial',
+ 'platform_localtime',
+ 'platform_clock_offset',
+ 'platform_enable_audio',
+ 'builder',
+ 'boot_method',
+ 'kernel_kernel',
+ 'kernel_initrd',
+ 'kernel_args',
+ 'grub_cmdline',
+ 'otherConfig']
+
+ VM_methods = ['clone',
+ 'start',
+ 'pause',
+ 'unpause',
+ 'clean_shutdown',
+ 'clean_reboot',
+ 'hard_shutdown',
+ 'hard_reboot',
+ 'suspend',
+ 'resume']
+
+ VM_funcs = ['get_by_name_label']
+
+ # 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_policy',
+ 'VCPUs_params',
+ 'VCPUs_features_required',
+ 'VCPUs_features_can_use',
+ 'VCPUs_features_force_on',
+ 'VCPUs_features_force_off',
+ 'actions_after_shutdown',
+ 'actions_after_reboot',
+ 'actions_after_suspend',
+ 'actions_after_crash',
+ 'bios_boot',
+ 'platform_std_VGA',
+ 'platform_serial',
+ 'platform_localtime',
+ 'platform_clock_offset',
+ 'platform_enable_audio',
+ 'builder',
+ 'boot_method',
+ 'kernel_kernel',
+ 'kernel_initrd',
+ 'kernel_args',
+ 'grub_cmdline',
+ 'PCI_bus',
+ 'otherConfig']
+
+ # 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.state)
+
+ def vm_get_resident_on(self, session, vm_ref):
+ return xen_api_success(XendNode.instance().uuid)
+
+ def vm_get_memory_actual(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_todo() # unsupported by xc
+
+ 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_VCPUs_number(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success(dom.getVCpuCount())
+
+ def vm_get_VCPUs_utilisation(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success(dom.get_vcpus_util())
+
+ def vm_get_VCPUs_features_required(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_todo() # unsupported by xc
+
+ def vm_get_VCPUs_can_use(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_todo() # unsupported by xc
+
+ 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_PCI_bus(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_todo() # unsupported by xc
+
+ def vm_get_tools_version(self, session, 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, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_todo()
+
+ def vm_get_memory_dynamic_max(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_todo()
+
+ def vm_get_memory_dynamic_min(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_todo()
+
+ def vm_get_VCPUs_policy(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_todo() # need to access scheduler
+
+ def vm_get_VCPUs_params(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_todo() # need access to scheduler
+
+ def vm_get_VCPUs_features_force_on(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_todo()
+
+ def vm_get_VCPUs_features_force_off(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_todo()
+
+ 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_bios_boot(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success(dom.get_bios_boot())
+
+ def vm_get_platform_std_VGA(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_todo()
+
+ def vm_get_platform_serial(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_todo()
+
+ def vm_get_platform_localtime(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_todo()
+
+ def vm_get_platform_clock_offset(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_todo()
+
+ def vm_get_platform_enable_audio(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_todo()
+
+ def vm_get_builder(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_todo()
+
+ def vm_get_boot_method(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success('')
+
+ def vm_get_kernel_kernel(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success('')
+
+ def vm_get_kernel_initrd(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success('')
+
+ def vm_get_kernel_args(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success('')
+
+ def vm_get_grub_cmdline(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success('')
+
+ def vm_get_otherConfig(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_todo()
+
+ def vm_set_name_label(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ def vm_set_name_description(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ def vm_set_user_version(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ def vm_set_is_a_template(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ def vm_set_memory_dynamic_max(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ def vm_set_memory_dynamic_min(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ def vm_set_vcpus_policy(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ def vm_set_vcpus_params(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ def vm_set_vcpus_features_force_on(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ def vm_set_vcpus_features_force_off(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ def vm_set_actions_after_shutdown(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ def vm_set_actions_after_reboot(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ def vm_set_actions_after_suspend(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ def vm_set_actions_after_crash(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ def vm_set_bios_boot(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ def vm_set_platform_std_VGA(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ def vm_set_platform_serial(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ def vm_set_platform_localtime(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ def vm_set_platform_clock_offset(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ def vm_set_platform_enable_audio(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ def vm_set_builder(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ def vm_set_boot_method(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ def vm_set_kernel_kernel(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ def vm_set_kernel_initrd(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ def vm_set_kernel_args(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ def vm_set_grub_cmdline(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ def vm_set_otherConfig(self, session, vm_ref):
+ dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+ return xen_api_success_void()
+
+ # class methods
+ def vm_get_all(self, session):
+ refs = [d.get_uuid() for d in XendDomain.instance().list()]
+ 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_error(XEND_ERROR_VM_INVALID)
+
+ def vm_create(self, session, vm_struct):
+ xendom = XendDomain.instance()
+ domuuid = xendom.create_domain(vm_struct)
+ return xen_api_success(domuuid)
+
+ # object methods
+ def vm_to_xml(self, session, vm_ref):
+ return xen_api_todo()
+
+ 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(XEND_ERROR_VM_INVALID)
+
+ 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': 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_static_min(),
+ 'memory_dynamic_max': xeninfo.get_memory_static_max(),
+ 'memory_actual': xeninfo.get_memory_static_min(),
+ 'vcpus_policy': xeninfo.get_vcpus_policy(),
+ 'vcpus_params': xeninfo.get_vcpus_params(),
+ 'vcpus_number': xeninfo.getVCpuCount(),
+ 'vcpus_utilisation': xeninfo.get_vcpus_util(),
+ 'vcpus_features_required': [],
+ 'vcpus_features_can_use': [],
+ 'vcpus_features_force_on': [],
+ 'vcpus_features_force_off': [],
+ '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(),
+ 'VIFs': xeninfo.get_vifs(),
+ 'VBDs': xeninfo.get_vbds(),
+ 'VTPMs': xeninfo.get_vtpms(),
+ 'bios_boot': xeninfo.get_bios_boot(),
+ 'platform_std_VGA': xeninfo.get_platform_std_vga(),
+ 'platform_serial': xeninfo.get_platform_serial(),
+ 'platform_localtime': xeninfo.get_platform_localtime(),
+ 'platform_clock_offset': xeninfo.get_platform_clock_offset(),
+ 'platform_enable_audio': xeninfo.get_platform_enable_audio(),
+ 'builder': xeninfo.get_builder(),
+ 'boot_method': xeninfo.get_boot_method(),
+ 'kernel_kernel': xeninfo.get_kernel_image(),
+ 'kernel_initrd': xeninfo.get_kernel_initrd(),
+ 'kernel_args': xeninfo.get_kernel_args(),
+ 'grub_cmdline': xeninfo.get_grub_cmdline(),
+ 'PCI_bus': xeninfo.get_pci_bus(),
+ 'tools_version': xeninfo.get_tools_version(),
+ 'otherConfig': xeninfo.get_other_config()
+ }
+ return xen_api_success(record)
+
+ def vm_clean_reboot(self, session, vm_ref):
+ xendom = XendDomain.instance()
+ xeninfo = xendom.get_vm_by_uuid(vm_ref)
+ 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)
+ 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 do_vm_func("domain_delete", vm_ref)
+ def vm_hard_reboot(self, session, vm_ref):
+ return xen_api_error(XEND_ERROR_UNSUPPORTED)
+ def vm_hard_shutdown(self, session, vm_ref):
+ return do_vm_func("domain_destroy", vm_ref)
+ def vm_pause(self, session, vm_ref):
+ return do_vm_func("domain_pause", vm_ref)
+ def vm_resume(self, session, vm_ref, start_paused):
+ return do_vm_func("domain_resume", vm_ref)
+ def vm_start(self, session, vm_ref):
+ return do_vm_func("domain_start", vm_ref)
+ def vm_suspend(self, session, vm_ref):
+ return do_vm_func("domain_suspend", vm_ref)
+ def vm_unpause(self, session, vm_ref):
+ return do_vm_func("domain_unpause", vm_ref)
+
+ # Xen API: Class VDI
+ # ----------------------------------------------------------------
+ # TODO: NOT IMPLEMENTED.
+
+ # Xen API: Class VBD
+ # ----------------------------------------------------------------
+
+ VBD_attr_ro = ['image',
+ 'IO_bandwidth_incoming_kbs',
+ 'IO_bandwidth_outgoing_kbs']
+ VBD_attr_rw = ['VM',
+ 'VDI',
+ 'device',
+ 'mode',
+ 'driver']
+
+ VBD_attr_inst = VBD_attr_rw + ['image']
+
+ # 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(XEND_ERROR_VBD_INVALID)
+ cfg = vm.get_dev_xenapi_config('vbd', vbd_ref)
+ if not cfg:
+ return xen_api_error(XEND_ERROR_VBD_INVALID)
+ return xen_api_success(cfg)
+
+ # class methods
+ def vbd_create(self, session, vbd_struct):
+ xendom = XendDomain.instance()
+ if not xendom.is_valid_vm(vbd_struct['VM']):
+ return xen_api_error(XEND_ERROR_DOMAIN_INVALID)
+
+ dom = xendom.get_vm_by_uuid(vbd_struct['VM'])
+ vbd_ref = ''
+ try:
+ if not vbd_struct.get('VDI', None):
+ # this is a traditional VBD without VDI and SR
+ vbd_ref = dom.create_vbd(vbd_struct)
+ else:
+ # new VBD via VDI/SR
+ vdi_ref = vbd_struct.get('VDI')
+ sr = XendNode.instance().get_sr()
+ vdi_image = sr.xen_api_get_by_uuid(vdi_ref)
+ if not vdi_image:
+ return xen_api_error(XEND_ERROR_VDI_INVALID)
+ vdi_image = vdi_image.qcow_path
+ vbd_ref = dom.create_vbd_with_vdi(vbd_struct, vdi_image)
+ except XendError:
+ return xen_api_todo()
+
+ xendom.managed_config_save(dom)
+ return xen_api_success(vbd_ref)
+
+ # attributes (rw)
+ def vbd_get_vm(self, session, vbd_ref):
+ xendom = XendDomain.instance()
+ return xen_api_success(xendom.get_dev_property('vbd', vbd_ref, 'VM'))
+
+ def vbd_get_vdi(self, session, vbd_ref):
+ return xen_api_todo()
+
+ def vbd_get_device(self, session, vbd_ref):
+ xendom = XendDomain.instance()
+ return xen_api_success(xendom.get_dev_property('vbd', vbd_ref,
+ 'device'))
+ def vbd_get_mode(self, session, vbd_ref):
+ xendom = XendDomain.instance()
+ return xen_api_success(xendom.get_dev_property('vbd', vbd_ref,
+ 'mode'))
+ def vbd_get_driver(self, session, vbd_ref):
+ xendom = XendDomain.instance()
+ return xen_api_success(xendom.get_dev_property('vbd', vbd_ref,
+ 'driver'))
+
+ # Xen API: Class VIF
+ # ----------------------------------------------------------------
+
+ VIF_attr_ro = ['network_read_kbs',
+ 'network_write_kbs',
+ 'IO_bandwidth_incoming_kbs',
+ 'IO_bandwidth_outgoing_kbs']
+ VIF_attr_rw = ['name',
+ 'type',
+ 'device',
+ 'network',
+ 'VM',
+ 'MAC',
+ 'MTU']
+
+ VIF_attr_inst = VIF_attr_rw
+
+ # 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(XEND_ERROR_VIF_INVALID)
+ cfg = vm.get_dev_xenapi_config('vif', vif_ref)
+ if not cfg:
+ return xen_api_error(XEND_ERROR_VIF_INVALID)
+ valid_vif_keys = self.VIF_attr_ro + self.VIF_attr_rw + \
+ self.Base_attr_ro + self.Base_attr_rw
+ for k in cfg.keys():
+ if k not in valid_vif_keys:
+ del cfg[k]
+
+ return xen_api_success(cfg)
+
+ # class methods
+ def vif_create(self, session, vif_struct):
+ xendom = XendDomain.instance()
+ if xendom.is_valid_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:
+ return xen_api_error(XEND_ERROR_TODO)
+ else:
+ return xen_api_error(XEND_ERROR_DOMAIN_INVALID)
+
+
+ # Xen API: Class VDI
+ # ----------------------------------------------------------------
+ VDI_attr_ro = ['VBDs',
+ 'physical_utilisation',
+ 'sector_size',
+ 'type',
+ 'parent',
+ 'children']
+ VDI_attr_rw = ['name_label',
+ 'name_description',
+ 'SR',
+ 'virtual_size',
+ 'sharable',
+ 'read_only']
+ VDI_attr_inst = VDI_attr_ro + VDI_attr_rw
+
+ VDI_methods = ['snapshot']
+ VDI_funcs = ['get_by_name_label']
+
+ def vdi_get_VBDs(self, session, vdi_ref):
+ return xen_api_todo()
+
+ def vdi_get_physical_utilisation(self, session, vdi_ref):
+ sr = XendNode.instance().get_sr()
+ image = sr.xen_api_get_by_uuid(vdi_ref)
+ return xen_api_success(image.get_physical_utilisation())
+
+ def vdi_get_sector_size(self, session, vdi_ref):
+ sr = XendNode.instance().get_sr()
+ image = sr.xen_api_get_by_uuid(vdi_ref)
+ return xen_api_success(image.sector_size)
+
+ def vdi_get_type(self, session, vdi_ref):
+ sr = XendNode.instance().get_sr()
+ image = sr.xen_api_get_by_uuid(vdi_ref)
+ return xen_api_success(image.type)
+
+ def vdi_get_parent(self, session, vdi_ref):
+ sr = XendNode.instance().get_sr()
+ image = sr.xen_api_get_by_uuid(vdi_ref)
+ return xen_api_success(image.parent)
+
+ def vdi_get_children(self, session, vdi_ref):
+ sr = XendNode.instance().get_sr()
+ image = sr.xen_api_get_by_uuid(vdi_ref)
+ return xen_api_success(image.children)
+
+ def vdi_get_name_label(self, session, vdi_ref):
+ sr = XendNode.instance().get_sr()
+ image = sr.xen_api_get_by_uuid(vdi_ref)
+ return xen_api_success(image.name_label)
+
+ def vdi_get_name_description(self, session, vdi_ref):
+ sr = XendNode.instance().get_sr()
+ image = sr.xen_api_get_by_uuid(vdi_ref)
+ return xen_api_success(image.name_description)
+
+ def vdi_get_sr(self, session, vdi_ref):
+ sr = XendNode.instance().get_sr()
+ return xen_api_success(sr.uuid)
+
+ def vdi_get_virtual_size(self, session, vdi_ref):
+ sr = XendNode.instance().get_sr()
+ image = sr.xen_api_get_by_uuid(vdi_ref)
+ return xen_api_success(image.virtual_size)
+
+ def vdi_get_sharable(self, session, vdi_ref):
+ sr = XendNode.instance().get_sr()
+ image = sr.xen_api_get_by_uuid(vdi_ref)
+ return xen_api_success(image.sharable)
+
+ def vdi_get_read_only(self, session, vdi_ref):
+ sr = XendNode.instance().get_sr()
+ image = sr.xen_api_get_by_uuid(vdi_ref)
+ return xen_api_success(image.sharable)
+
+ def vdi_set_name_label(self, session, vdi_ref, value):
+ sr = XendNode.instance().get_sr()
+ image = sr.xen_api_get_by_uuid(vdi_ref)
+ image.name_label = value
+ return xen_api_success_void()
+
+ def vdi_set_name_description(self, session, vdi_ref, value):
+ sr = XendNode.instance().get_sr()
+ image = sr.xen_api_get_by_uuid(vdi_ref)
+ image.name_description = value
+ return xen_api_success_void()
+
+ def vdi_set_sr(self, session, vdi_ref, value):
+ return xen_api_error(XEND_ERROR_UNSUPPORTED)
+
+ 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):
+ return xen_api_todo()
+ def vdi_set_read_only(self, session, vdi_ref, value):
+ return xen_api_todo()
+
+ # Object Methods
+ def vdi_snapshot(self, session, vdi_ref):
+ return xen_api_todo()
+
+ def vdi_destroy(self, session, vdi_ref):
+ sr = XendNode.instance().get_sr()
+ sr.destroy_image(vdi_ref)
+ return xen_api_success_void()
+
+ def vdi_to_xml(self, session, vdi_ref):
+ return xen_api_todo()
+
+ def vdi_get_record(self, session, vdi_ref):
+ sr = XendNode.instance().get_sr()
+ image = sr.xen_api_get_by_uuid(vdi_ref)
+ if image:
+ return xen_api_success({
+ 'uuid': vdi_ref,
+ 'name_label': image.name_label,
+ 'name_description': image.name_description,
+ 'SR': sr.uuid,
+ 'VBDs': [], # TODO
+ 'virtual_size': image.virtual_size,
+ 'physical_utilisation': image.physical_utilisation,
+ 'sector_size': image.sector_size,
+ 'type': image.type,
+ 'parent': image.parent,
+ 'children': image.children,
+ 'sharable': image.sharable,
+ 'read_only': image.read_only,
+ })
+
+ return xen_api_error(XEND_ERROR_VDI_INVALID)
+
+ # Class Functions
+ def vdi_create(self, session, vdi_struct):
+ sr = XendNode.instance().get_sr()
+ sr_ref = vdi_struct['SR']
+ if sr.uuid != sr_ref:
+ return xen_api_error(XEND_ERROR_SR_INVALID)
+
+ vdi_uuid = sr.create_image(vdi_struct)
+ return xen_api_success(vdi_uuid)
+
+ def vdi_get_all(self, session):
+ sr = XendNode.instance().get_sr()
+ return xen_api_success(sr.list_images())
+
+ def vdi_get_by_name_label(self, session, name):
+ sr = XendNode.instance().get_sr()
+ image_uuid = sr.xen_api_get_by_name_label(name)
+ if image_uuid:
+ return xen_api_success(image_uuid)
+
+ return xen_api_error(XEND_ERROR_VDI_INVALID)
+
+
+ # Xen API: Class VTPM
+ # ----------------------------------------------------------------
+
+ VTPM_attr_rw = [ ]
+ VTPM_attr_ro = ['VM',
+ 'backend',
+ 'instance',
+ 'driver']
+
+ VTPM_attr_inst = VTPM_attr_rw
+
+ # 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(XEND_ERROR_VTPM_INVALID)
+ cfg = vm.get_dev_xenapi_config('vtpm', vtpm_ref)
+ if not cfg:
+ return xen_api_error(XEND_ERROR_VTPM_INVALID)
+ valid_vtpm_keys = self.VTPM_attr_ro + self.VTPM_attr_rw + \
+ self.Base_attr_ro + self.Base_attr_rw
+ for k in cfg.keys():
+ if k not in valid_vtpm_keys:
+ del cfg[k]
+
+ return xen_api_success(cfg)
+
+ # Class Functions
+ def vtpm_get_instance(self, session, vtpm_ref):
+ xendom = XendDomain.instance()
+ vm = xendom.get_vm_with_dev_uuid('vtpm', vtpm_ref)
+ if not vm:
+ return xen_api_error(XEND_ERROR_VTPM_INVALID)
+ cfg = vm.get_dev_xenapi_config('vtpm', vtpm_ref)
+ if not cfg:
+ return xen_api_error(XEND_ERROR_VTPM_INVALID)
+ if cfg.has_key('instance'):
+ instance = cfg['instance']
+ else:
+ instance = -1
+ return xen_api_success(instance)
+
+ def vtpm_get_driver(self, session, vtpm_ref):
+ xendom = XendDomain.instance()
+ vm = xendom.get_vm_with_dev_uuid('vtpm', vtpm_ref)
+ if not vm:
+ return xen_api_error(XEND_ERROR_VTPM_INVALID)
+ cfg = vm.get_dev_xenapi_config('vtpm', vtpm_ref)
+ if not cfg:
+ return xen_api_error(XEND_ERROR_VTPM_INVALID)
+ if cfg.has_key('type'):
+ driver = cfg['type']
+ else:
+ driver = "Unknown"
+ return xen_api_success(driver)
+
+ 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(XEND_ERROR_VTPM_INVALID)
+ cfg = vm.get_dev_xenapi_config('vtpm', vtpm_ref)
+ if not cfg:
+ return xen_api_error(XEND_ERROR_VTPM_INVALID)
+ if cfg.has_key('backend'):
+ backend = cfg['backend']
+ else:
+ backend = "Domain-0"
+ return xen_api_success(backend)
+
+ def vtpm_get_VM(self, session, vtpm_ref):
+ xendom = XendDomain.instance()
+ return xen_api_success(xendom.get_dev_property('vtpm', vtpm_ref, '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'])
+ vtpm_ref = dom.create_vtpm(vtpm_struct)
+ xendom.managed_config_save(dom)
+ return xen_api_success(vtpm_ref)
+ else:
+ return xen_api_error(XEND_ERROR_DOMAIN_INVALID)
+
+
+ # Xen API: Class SR
+ # ----------------------------------------------------------------
+ SR_attr_ro = ['VDIs',
+ 'virtual_allocation',
+ 'physical_utilisation',
+ 'physical_size',
+ 'type',
+ 'location']
+
+ SR_attr_rw = ['name_label',
+ 'name_description']
+
+ SR_attr_inst = ['physical_size',
+ 'type',
+ 'location',
+ 'name_label',
+ 'name_description']
+
+ SR_methods = ['clone']
+ SR_funcs = ['get_by_name_label']
+
+ # Class Functions
+ def sr_get_all(self, session):
+ sr = XendNode.instance().get_sr()
+ return xen_api_success([sr.uuid])
+
+ def sr_get_by_name_label(self, session, label):
+ sr = XendNode.instance().get_sr()
+ if sr.name_label != label:
+ return xen_api_error(XEND_ERROR_SR_INVALID)
+ return xen_api_success([sr.uuid])
+
+ def sr_create(self, session):
+ return xen_api_error(XEND_ERROR_UNSUPPORTED)
+
+ def sr_get_by_uuid(self, session):
+ return xen_api_success(XendNode.instance().get_sr().uuid)
+
+ # Class Methods
+ def sr_clone(self, session, sr_ref):
+ return xen_api_error(XEND_ERROR_UNSUPPORTED)
+ def sr_destroy(self, session, sr_ref):
+ return xen_api_error(XEND_ERROR_UNSUPPORTED)
+
+ def sr_to_xml(self, session, sr_ref):
+ return xen_api_todo()
+
+ def sr_get_record(self, session, sr_ref):
+ sr = XendNode.instance().get_sr()
+ return xen_api_success({
+ 'uuid': sr.uuid,
+ 'name_label': sr.name_label,
+ 'name_description': sr.name_description,
+ 'VDIs': sr.list_images(),
+ 'virtual_allocation': sr.used_space_bytes(),
+ 'physical_utilisation': sr.used_space_bytes(),
+ 'physical_size': sr.total_space_bytes(),
+ 'type': sr.type,
+ 'location': sr.location
+ })
+
+ # Attribute acceess
+ def sr_get_vdis(self, session, sr_ref):
+ sr = XendNode.instance().get_sr()
+ return xen_api_success(sr.list_images())
+
+ def sr_get_virtual_allocation(self, session, sr_ref):
+ sr = XendNode.instance().get_sr()
+ return sr.used_space_bytes()
+
+ def sr_get_physical_utilisation(self, session, sr_ref):
+ sr = XendNode.instance().get_sr()
+ return sr.used_space_bytes()
+
+ def sr_get_physical_size(self, session, sr_ref):
+ sr = XendNode.instance().get_sr()
+ return sr.total_space_bytes()
+
+ def sr_get_type(self, session, sr_ref):
+ sr = XendNode.instance().get_sr()
+ return xen_api_success(sr.type)
+
+ def sr_get_location(self, session, sr_ref):
+ sr = XendNode.instance().get_sr()
+ return xen_api_success(sr.location)
+
+ def sr_get_name_label(self, session, sr_ref):
+ sr = XendNode.instance().get_sr()
+ return xen_api_success(sr.name_label)
+
+ def sr_get_name_description(self, session, sr_ref):
+ sr = XendNode.instance().get_sr()
+ return xen_api_success(sr.name_description)
+
+ def sr_set_name_label(self, session, sr_ref, value):
+ sr = XendNode.instance().get_sr()
+ sr.name_label = value
+ return xen_api_success_void()
+
+ def sr_set_name_description(self, session, sr_ref, value):
+ sr = XendNode.instance().get_sr()
+ sr.name_description = value
+ return xen_api_success_void()
+
+#
+# Auto generate some stubs based on XendAPI introspection
+#
+if __name__ == "__main__":
+ def output(line):
+ print ' ' + line
+
+ classes = ['VDI', 'SR']
+ for cls in classes:
+ ro_attrs = getattr(XendAPI, '%s_attr_ro' % cls, [])
+ rw_attrs = getattr(XendAPI, '%s_attr_rw' % cls, [])
+ methods = getattr(XendAPI, '%s_methods' % cls, [])
+ funcs = getattr(XendAPI, '%s_funcs' % cls, [])
+
+ ref = '%s_ref' % cls.lower()
+
+ for attr_name in ro_attrs + rw_attrs + XendAPI.Base_attr_ro:
+ getter_name = '%s_get_%s' % (cls.lower(), attr_name.lower())
+ output('def %s(self, session, %s):' % (getter_name, ref))
+ output(' return xen_api_todo()')
+
+ for attr_name in rw_attrs + XendAPI.Base_attr_rw:
+ setter_name = '%s_set_%s' % (cls.lower(), attr_name.lower())
+ output('def %s(self, session, %s, value):' % (setter_name, ref))
+ output(' return xen_api_todo()')
+
+ for method_name in methods + XendAPI.Base_methods:
+ method_full_name = '%s_%s' % (cls.lower(),method_name.lower())
+ output('def %s(self, session, %s):' % (method_full_name, ref))
+ output(' return xen_api_todo()')
+
+ for func_name in funcs + XendAPI.Base_funcs:
+ func_full_name = '%s_%s' % (cls.lower(), func_name.lower())
+ output('def %s(self, session):' % func_full_name)
+ output(' return xen_api_todo()')
diff --git a/tools/python/xen/xend/XendAPIConstants.py b/tools/python/xen/xend/XendAPIConstants.py
new file mode 100644
index 0000000000..875f948bcf
--- /dev/null
+++ b/tools/python/xen/xend/XendAPIConstants.py
@@ -0,0 +1,75 @@
+#============================================================================
+# 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.
+#============================================================================
+
+#
+# Xen API Enums
+#
+
+XEN_API_VM_POWER_STATE = [
+ 'Halted',
+ 'Paused',
+ 'Running',
+ 'Suspended',
+ 'ShuttingDown',
+ '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_UNKNOWN = 5
+
+XEN_API_CPU_FEATURE = [
+ 'FPU', 'VME', 'DE', 'PSE', 'TSC', 'MSR', 'PAE'
+ 'MCE', 'CX8', 'APIC', 'SEP', 'MTRR', 'PGE', 'MCA',
+ 'CMOV', 'PAT', 'PSE36', 'PN', 'CLFLSH', 'DTES',
+ 'ACPI', 'MMX', 'FXCR', 'XMM', 'XMM2', 'SELFSNOOP',
+ 'HT', 'ACC', 'IA64', 'SYSCALL', 'MP', 'NX', 'MMXEXT',
+ 'LM', '3DNOWEXT', '3DNOW', 'RECOVERY', 'LONGRUN',
+ 'LRTI', 'CXMMX', 'K6_MTRR', 'CYRIX_ARR', 'CENTAUR_MCR',
+ 'K8', 'K7', 'P3', 'P4', 'CONSTANT_TSC', 'FXSAVE_LEAK',
+ 'XMM3', 'MWAIT', 'DSCPL', 'EST', 'TM2', 'CID', 'CX16',
+ 'XTPR', 'XSTORE', 'XSTORE_EN', 'XCRYPT', 'XCRYPT_EN',
+ 'LAHF_LM', 'CMP_LEGACY'
+]
+
+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_BOOT_TYPE = [
+ 'bios',
+ 'grub',
+ 'kernel_external',
+ 'kernel_internal'
+]
+
+XEN_API_VBD_MODE = ['RO', 'RW']
+XEN_API_VDI_TYPE = ['system', 'user', 'ephemeral']
+XEN_API_DRIVER_TYPE = ['ioemu', 'paravirtualised']
diff --git a/tools/python/xen/xend/XendAuthSessions.py b/tools/python/xen/xend/XendAuthSessions.py
new file mode 100644
index 0000000000..152c8c5881
--- /dev/null
+++ b/tools/python/xen/xend/XendAuthSessions.py
@@ -0,0 +1,137 @@
+#============================================================================
+# 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
+
+try:
+ import PAM
+except ImportError:
+ log.warn("python-pam is required for XenAPI support.")
+
+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_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):
+ new_session = uuid.createString()
+ self.sessions[new_session] = (username, time.time())
+ return new_session
+
+ 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:
+ pam_auth = PAM.pam()
+ 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):
+ 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
+
+# Handy Authentication Decorators
+# -------------------------------
+def session_required(func):
+ def check_session(self, session, *args, **kwargs):
+ if instance().is_session_valid(session):
+ return func(self, session, *args, **kwargs)
+ else:
+ return {'Status': 'Failure',
+ 'ErrorDescription': XEND_ERROR_SESSION_INVALID}
+ return check_session
+
+
diff --git a/tools/python/xen/xend/XendBootloader.py b/tools/python/xen/xend/XendBootloader.py
index 84d946e800..879b39806d 100644
--- a/tools/python/xen/xend/XendBootloader.py
+++ b/tools/python/xen/xend/XendBootloader.py
@@ -14,8 +14,8 @@
import os, select, errno
import random
-import sxp
import shlex
+from xen.xend import sxp
from XendLogging import log
from XendError import VmError
@@ -38,7 +38,7 @@ def bootloader(blexec, disk, quiet = 0, blargs = None, imgcfg = None):
raise VmError(msg)
while True:
- fifo = "/var/lib/xen/xenbl.%s" %(random.randint(0, 32000),)
+ fifo = "/var/lib/xen/xenbl.%s" % random.randint(0, 32000)
if not os.path.exists(fifo):
break
os.mkfifo(fifo, 0600)
@@ -48,7 +48,7 @@ def bootloader(blexec, disk, quiet = 0, blargs = None, imgcfg = None):
args = [ blexec ]
if quiet:
args.append("-q")
- args.append("--output=%s" %(fifo,))
+ args.append("--output=%s" % fifo)
if blargs is not None:
args.extend(shlex.split(blargs))
args.append(disk)
diff --git a/tools/python/xen/xend/XendCheckpoint.py b/tools/python/xen/xend/XendCheckpoint.py
index 8e08fb425d..e8894933fb 100644
--- a/tools/python/xen/xend/XendCheckpoint.py
+++ b/tools/python/xen/xend/XendCheckpoint.py
@@ -8,21 +8,18 @@
import os
import re
import string
-import sxp
import threading
from struct import pack, unpack, calcsize
from xen.util.xpopen import xPopen3
-
import xen.util.auxbin
-
import xen.lowlevel.xc
-import balloon
-from XendError import XendError
-from XendLogging import log
-from XendDomainInfo import DEV_MIGRATE_STEP1, DEV_MIGRATE_STEP2
-from XendDomainInfo import DEV_MIGRATE_STEP3
+from xen.xend import balloon, sxp
+from xen.xend.XendError import XendError
+from xen.xend.XendLogging import log
+from xen.xend.XendConstants import *
+from xen.xend.XendConfig import XendConfig
SIGNATURE = "LinuxGuestRecord"
XC_SAVE = "xc_save"
@@ -43,13 +40,13 @@ def write_exact(fd, buf, errmsg):
def read_exact(fd, size, errmsg):
buf = ''
while size != 0:
- str = os.read(fd, size)
- if not len(str):
+ 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(str)
- buf = buf + str
+ size = size - len(readstr)
+ buf = buf + readstr
return buf
@@ -111,7 +108,7 @@ def save(fd, dominfo, network, live, dst):
raise Exception, exn
-def restore(xd, fd):
+def restore(xd, fd, dominfo = None):
signature = read_exact(fd, len(SIGNATURE),
"not a valid guest state file: signature read")
if signature != SIGNATURE:
@@ -131,7 +128,11 @@ def restore(xd, fd):
vmconfig = p.get_val()
- dominfo = xd.restore_(vmconfig)
+ if dominfo:
+ dominfo.update(XendConfig(sxp = vmconfig), refresh = False)
+ dominfo.resume()
+ else:
+ dominfo = xd.restore_(vmconfig)
store_port = dominfo.getStorePort()
console_port = dominfo.getConsolePort()
diff --git a/tools/python/xen/xend/XendConfig.py b/tools/python/xen/xend/XendConfig.py
new file mode 100644
index 0000000000..98f1f572fa
--- /dev/null
+++ b/tools/python/xen/xend/XendConfig.py
@@ -0,0 +1,872 @@
+#===========================================================================
+# 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 re
+import time
+
+from xen.xend import sxp
+from xen.xend import uuid
+from xen.xend.XendError import VmError
+from xen.xend.XendDevices import XendDevices
+from xen.xend.XendLogging import log
+from xen.xend.PrettyPrint import prettyprintstring
+from xen.xend.XendConstants import DOM_STATE_HALTED
+
+"""
+XendConfig API
+
+ XendConfig will try to mirror as closely the Xen API VM Struct
+ providing a backwards compatibility mode for SXP dumping, loading.
+
+"""
+
+
+LEGACY_CFG_TO_XENAPI_CFG = {
+ 'uuid': 'uuid',
+ 'vcpus': 'vcpus_number',
+ 'maxmem': 'memory_static_max',
+ 'memory': 'memory_static_min',
+ 'name': 'name_label',
+ 'on_poweroff': 'actions_after_shutdown',
+ 'on_reboot': 'actions_after_reboot',
+ 'on_crash': 'actions_after_crash',
+ 'bootloader': 'boot_method',
+ 'kernel_kernel': 'kernel_kernel',
+ 'kernel_initrd': 'kernel_initrd',
+ 'kernel_args': 'kernel_args',
+ }
+
+XENAPI_CFG_CUSTOM_TRANSLATE = [
+ 'vifs',
+ 'vbds',
+ ]
+
+XENAPI_HVM_CFG = {
+ 'platform_std_vga': 'std-vga',
+ 'platform_serial' : 'serial',
+ 'platform_localtime': 'localtime',
+ 'platform_enable_audio': 'soundhw',
+}
+
+XENAPI_UNSUPPORTED_IN_LEGACY_CFG = [
+ 'name_description',
+ 'user_version',
+ 'is_a_template',
+ 'memory_dynamic_min',
+ 'memory_dynamic_max',
+ 'memory_actual',
+ 'vcpus_policy',
+ 'vcpus_params',
+ 'vcpus_features_required',
+ 'vcpus_features_can_use',
+ 'vcpus_features_force_on',
+ 'vcpus_features_force_off',
+ 'actions_after_suspend',
+ 'bios_boot',
+ 'platform_std_vga',
+ 'platform_serial',
+ 'platform_localtime',
+ 'platform_clock_offset',
+ 'platform_enable_audio',
+ 'builder',
+ 'grub_cmdline',
+ 'pci_bus',
+ 'otherconfig'
+ ]
+
+
+# configuration params that need to be converted to ints
+# since the XMLRPC transport for Xen API does not use
+# 32 bit ints but string representation of 64 bit ints.
+XENAPI_INT_CFG = [
+ 'user_version',
+ 'vcpus_number',
+ 'memory_static_min',
+ 'memory_static_max',
+ 'memory_dynamic_min',
+ 'memory_dynamic_max',
+ 'tpm_instance',
+ 'tpm_backend',
+]
+
+##
+## Xend Configuration Parameters
+##
+
+
+# All parameters of VMs that may be configured on-the-fly, or at start-up.
+VM_CONFIG_ENTRIES = [
+ ('name', str),
+ ('on_crash', str),
+ ('on_poweroff', str),
+ ('on_reboot', str),
+ ('on_xend_start', str),
+ ('on_xend_stop', str),
+]
+
+# All entries written to the store. This is VM_CONFIG_ENTRIES, plus those
+# entries written to the store that cannot be reconfigured on-the-fly.
+VM_STORE_ENTRIES = [
+ ('uuid', str),
+ ('vcpus', int),
+ ('vcpu_avail', int),
+ ('memory', int),
+ ('maxmem', int),
+ ('start_time', float),
+]
+
+VM_STORED_ENTRIES = VM_CONFIG_ENTRIES + VM_STORE_ENTRIES
+
+# Configuration entries that we expect to round-trip -- be read from the
+# config file or xc, written to save-files (i.e. through sxpr), and reused as
+# config on restart or restore, all without munging. Some configuration
+# entries are munged for backwards compatibility reasons, or because they
+# don't come out of xc in the same form as they are specified in the config
+# file, so those are handled separately.
+
+ROUNDTRIPPING_CONFIG_ENTRIES = [
+ ('uuid', str),
+ ('vcpus', int),
+ ('vcpu_avail', int),
+ ('cpu_weight', int),
+ ('memory', int),
+ ('shadow_memory', int),
+ ('maxmem', int),
+ ('bootloader', str),
+ ('bootloader_args', str),
+ ('features', str),
+ ('localtime', int),
+]
+ROUNDTRIPPING_CONFIG_ENTRIES += VM_CONFIG_ENTRIES
+
+## Static Configuration
+
+STATIC_CONFIG_ENTRIES = [
+ ('cpu', int),
+ ('cpus', str),
+ ('image', list),
+ ('security', list), # TODO: what if null?
+]
+
+DEPRECATED_ENTRIES = [
+ ('restart', str),
+]
+
+##
+## Config Choices
+##
+
+CONFIG_RESTART_MODES = ('restart', 'destroy', 'preserve', 'rename-restart')
+CONFIG_OLD_DOM_STATES = ('running', 'blocked', 'paused', 'shutdown',
+ 'crashed', 'dying')
+
+##
+## Defaults
+##
+
+def DEFAULT_VCPUS(info):
+ if 'max_vcpu_id' in info: return int(info['max_vcpu_id']) + 1
+ else: return 1
+
+DEFAULT_CONFIGURATION = (
+ ('uuid', lambda info: uuid.createString()),
+ ('name', lambda info: 'Domain-' + info['uuid']),
+
+ ('on_poweroff', lambda info: 'destroy'),
+ ('on_reboot', lambda info: 'restart'),
+ ('on_crash', lambda info: 'restart'),
+ ('features', lambda info: ''),
+
+
+ ('memory', lambda info: 0),
+ ('shadow_memory',lambda info: 0),
+ ('maxmem', lambda info: 0),
+ ('bootloader', lambda info: None),
+ ('bootloader_args', lambda info: None),
+ ('backend', lambda info: []),
+ ('device', lambda info: {}),
+ ('image', lambda info: None),
+ ('security', lambda info: []),
+ ('on_xend_start', lambda info: 'ignore'),
+ ('on_xend_stop', lambda info: 'ignore'),
+
+ ('cpus', lambda info: []),
+ ('cpu_cap', lambda info: 0),
+ ('cpu_weight', lambda info: 256),
+ ('vcpus', lambda info: DEFAULT_VCPUS(info)),
+ ('online_vcpus', lambda info: info['vcpus']),
+ ('max_vcpu_id', lambda info: info['vcpus']-1),
+ ('vcpu_avail', lambda info: (1<<info['vcpus'])-1),
+
+ # New for Xen API
+ ('kernel_kernel', lambda info: ''),
+ ('kernel_initrd', lambda info: ''),
+ ('kernel_args', lambda info: ''),
+
+)
+
+class XendConfigError(VmError):
+ def __str__(self):
+ return 'Invalid Configuration: %s' % str(self.value)
+
+##
+## XendConfig SXP Config Compat
+##
+
+class XendSXPConfig:
+ def get_domid(self):
+ pass
+ def get_handle(self):
+ return self['uuid']
+
+
+##
+## XendConfig Class (an extended dictionary)
+##
+
+class XendConfig(dict):
+ """ Generic Configuration Parser accepting SXP, Python or XML.
+ This is a dictionary-like object that is populated.
+
+ @ivar legacy: dictionary holding legacy xen domain info
+ @ivar xenapi: dictionary holding xen api config info
+ """
+
+ def __init__(self, filename = None, fd = None,
+ sxp = None, xml = None, pycfg = None, xenapi_vm = None,
+ cfg = {}):
+ """Constructor. Provide either the filename, fd or sxp.
+
+ @keyword filename: filename of an SXP file
+ @keyword fd: file descriptor of an SXP file
+ @keyword sxp: a list of list of a parsed SXP
+ @keyword xml: an XML tree object
+ @keyword xenapi_vm: a struct passed from an XMLRPC call (Xen API)
+ @keyword cfg: a dictionary of configuration (eg. from xc)
+ """
+ format = 'unknown'
+
+ self.xenapi = {}
+
+ if filename and not fd:
+ fd = open(filename, 'r')
+
+ if fd:
+ format = self._detect_format(fd)
+
+ if fd:
+ if format == 'sxp':
+ sxp = self._read_sxp(fd)
+ elif format == 'python' and filename != None:
+ pycfg = self._read_python(filename)
+ elif format == 'python' and filename == None:
+ raise XendConfigError("Python files must be passed as a "
+ "filename rather than file descriptor.")
+ elif format == 'xml':
+ xml = self._read_xml(fd)
+ else:
+ raise XendConfigError("Unable to determine format of file")
+
+ if sxp:
+ cfg = self._populate_from_sxp(sxp)
+ if xml:
+ cfg = self._populate_from_xml(xml)
+ if pycfg:
+ cfg = self._populate_from_python_config(pycfg)
+ if xenapi_vm:
+ cfg = self._populate_from_xenapi_vm(xenapi_vm)
+
+ if cfg:
+ self.update(cfg)
+
+ if xenapi_vm:
+ self.xenapi.update(xenapi_vm)
+
+ log.debug('XendConfig: %s' % str(self))
+ self.validate()
+
+ #
+ # Xen API Attribute Access
+ #
+
+ def __getattr__(self, name):
+ try:
+ return dict.__getattr__(self, name)
+ except AttributeError:
+ try:
+ return self.__dict__['xenapi'][name]
+ except KeyError:
+ raise AttributeError("XendConfig Xen API has no attribute "
+ "'%s'" % name)
+
+
+ def __setattr__(self, name, value):
+ try:
+ return dict.__setattr__(self, name, value)
+ except AttributeError:
+ self.xenapi[name] = value
+ #self.set_legacy_api_with_xen_api_value(name, value)
+
+ def __delattr__(self, name):
+ try:
+ dict.__delattr__(self, name)
+ except AttributeError:
+ del self.xenapi[name]
+ #self.del_legacy_api_with_xen_api_key(name)
+
+
+ """
+ #
+ # Legacy API Attribute Access
+ #
+
+ def __getitem__(self, key):
+ try:
+ return self.legacy[key]
+ except KeyError:
+ raise AttributeError, "XendConfig Legacy has no attribute '%s'"\
+ % key
+
+ def __setitem__(self, key, value):
+ self.legacy[key] = value
+ self.set_xen_api_with_legacy_api_value(key, value)
+
+ def __delitem__(self, key):
+ del self.legacy[key]
+ self.del_xen_api_with_legacy_api_key(key)
+ """
+
+
+ def _detect_format(self, fd):
+ """Detect the format of the configuration passed.
+
+ @param fd: file descriptor of contents to detect
+ @rtype: string, 'sxp', 'xml', 'python' or 'unknown'
+ """
+ format = 'unknown'
+
+ fd.seek(0)
+ for line in fd:
+ stripped = line.strip()
+ if stripped:
+ if re.search(r'^\(', stripped):
+ format = 'sxp'
+ elif re.search(r'^\<?xml', stripped):
+ format = 'xml'
+ else:
+ format = 'python'
+ break
+
+ fd.seek(0)
+ return format
+
+ def _read_sxp(self, fd):
+ """ Read and parse SXP (from SXP to list of lists)
+
+ @rtype: list of lists.
+ """
+ try:
+ parsed = sxp.parse(fd)[0]
+ return parsed
+ except:
+ raise
+ return None
+
+ def _read_xml(self, fd):
+ """TODO: Read and parse XML (from XML to dict)
+
+ @rtype: dict
+ """
+ raise NotImplementedError
+
+ def _read_python(self, filename):
+ """Read and parse python module that represents the config.
+
+ @rtype: dict
+ """
+ cfg_globals = {}
+ execfile(filename, cfg_globals, {})
+ return cfg_globals
+
+ def _populate_from_sxp(self, parsed):
+ """ Populate this XendConfig using the parsed SXP.
+
+ @rtype: dictionary
+ """
+ cfg = {}
+
+ # First step is to convert deprecated options to
+ # current equivalents.
+
+ restart = sxp.child_value(parsed, '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)
+
+ # Only extract options we know about.
+ all_params = VM_CONFIG_ENTRIES + ROUNDTRIPPING_CONFIG_ENTRIES + \
+ STATIC_CONFIG_ENTRIES
+
+ for key, typeconv in all_params:
+ val = sxp.child_value(parsed, key)
+ if val:
+ try:
+ cfg[key] = typeconv(val)
+ except ValueError:
+ pass
+
+ # Manually extract other complex configuration
+ # options.
+
+ cfg['backend'] = []
+ for c in sxp.children(parsed, 'backend'):
+ cfg['backend'].append(sxp.name(sxp.child0(c)))
+
+ cfg['device'] = {}
+ for dev in sxp.children(parsed, 'device'):
+ config = sxp.child0(dev)
+ dev_type = sxp.name(config)
+ dev_info = {}
+ for opt, val in config[1:]:
+ dev_info[opt] = val
+ log.debug("XendConfig: reading device: %s" % dev_info)
+ # create uuid if it doesn't
+ dev_uuid = dev_info.get('uuid', uuid.createString())
+ dev_info['uuid'] = dev_uuid
+ cfg['device'][dev_uuid] = (dev_type, dev_info)
+
+ #cfg['device'].append((sxp.name(config), config))
+
+
+ # Extract missing data from configuration entries
+ if 'image' in cfg:
+ image_vcpus = sxp.child_value(cfg['image'], 'vcpus')
+ if image_vcpus is not None:
+ try:
+ if 'vcpus' not in cfg:
+ cfg['vcpus'] = int(image_vcpus)
+ elif cfg['vcpus'] != int(image_vcpus):
+ cfg['vcpus'] = int(image_vcpus)
+ log.warn('Overriding vcpus from %d to %d using image'
+ 'vcpus value.', cfg['vcpus'])
+ except ValueError, e:
+ raise XendConfigError('integer expeceted: %s: %s' %
+ str(cfg['image']), 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' string to list of ints
+ # 'cpus' 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]
+ try:
+ if 'cpus' in cfg:
+ cpus = []
+ for c in cfg['cpus'].split(','):
+ if c.find('-') != -1:
+ (x, y) = c.split('-')
+ for i in range(int(x), int(y)+1):
+ cpus.append(int(i))
+ else:
+ # remove this element from the list
+ if c[0] == '^':
+ cpus = [x for x in cpus if x != int(c[1:])]
+ else:
+ cpus.append(int(c))
+
+ cfg['cpus'] = cpus
+ except ValueError, e:
+ raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
+
+ # Parse image SXP outside of image.py
+ # - used to be only done in image.py
+ if 'image' in cfg:
+ cfg['kernel_kernel'] = sxp.child_value(cfg['image'], 'kernel','')
+ cfg['kernel_initrd'] = sxp.child_value(cfg['image'], 'ramdisk','')
+ kernel_args = sxp.child_value(cfg['image'], 'args', '')
+
+ # attempt to extract extra arguments from SXP config
+ arg_ip = sxp.child_value(cfg['image'], 'ip')
+ if arg_ip: kernel_args += ' ip=%s' % arg_ip
+ arg_root = sxp.child_value(cfg['image'], 'root')
+ if arg_root: kernel_args += ' root=%s' % arg_root
+
+ cfg['kernel_args'] = kernel_args
+
+ # TODO: get states
+ old_state = sxp.child_value(parsed, 'state')
+ if old_state:
+ for i in range(len(CONFIG_OLD_DOM_STATES)):
+ cfg[CONFIG_OLD_DOM_STATES[i]] = (old_state[i] != '-')
+
+ # Xen API extra cfgs
+ # ------------------
+ cfg['vif_refs'] = []
+ cfg['vbd_refs'] = []
+ cfg['vtpm_refs'] = []
+ for dev_uuid, (dev_type, dev_info) in cfg['device'].items():
+ if dev_type == 'vif':
+ cfg['vif_refs'].append(dev_uuid)
+ elif dev_type in ('vbd','tap'):
+ cfg['vbd_refs'].append(dev_uuid)
+ elif dev_type == 'vtpm':
+ cfg['vtpm_refs'].append(dev_uuid)
+
+ return cfg
+
+
+ def _populate_from_xenapi_vm(self, xenapi_vm):
+ cfg = {}
+
+ for cfgkey, apikey in LEGACY_CFG_TO_XENAPI_CFG.items():
+ try:
+ if apikey in XENAPI_INT_CFG:
+ cfg[cfgkey] = int(xenapi_vm[apikey])
+ else:
+ cfg[cfgkey] = xenapi_vm[apikey]
+ except KeyError:
+ pass
+
+ # Reconstruct image SXP
+ # TODO: get rid of SXP altogether from here
+ sxp_image = ['linux']
+ if xenapi_vm['kernel_kernel']:
+ sxp_image.append(['kernel', xenapi_vm['kernel_kernel']])
+ if xenapi_vm['kernel_initrd']:
+ sxp_image.append(['ramdisk', xenapi_vm['kernel_initrd']])
+ if xenapi_vm['kernel_args']:
+ sxp_image.append(['args', xenapi_vm['kernel_args']])
+
+ cfg['image'] = prettyprintstring(sxp_image)
+
+ # make sure device structures are there.
+ if 'device' not in cfg:
+ cfg['device'] = {}
+ if 'vif_refs' not in cfg:
+ cfg['vif_refs'] = []
+ if 'vbd_refs' not in cfg:
+ cfg['vbd_refs'] = []
+ if 'vtpm_refs' not in cfg:
+ cfg['vtpm_refs'] = []
+
+ return cfg
+
+
+ def _sync_xen_api_from_legacy_api(self):
+ """ Sync all the attributes that is supported by the Xen API
+ from the legacy API configuration.
+ """
+ for cfgkey, apikey in LEGACY_CFG_TO_XENAPI_CFG.items():
+ if cfgkey in self:
+ self.xenapi[apikey] = self[cfgkey]
+
+ def _sync_legacy_api_from_xen_api(self):
+ for cfgkey, apikey in LEGACY_CFG_TO_XENAPI_CFG.items():
+ if apikey in self.xenapi:
+ self[cfgkey] = self.xenapi[apikey]
+
+
+ def _populate_from_xml(self, parsed_xml):
+ raise NotImplementedError
+
+ def _populate_from_python_config(self, parsed_py):
+ raise NotImplementedError
+
+ def _get_old_state_string(self):
+ 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 get_sxp(self, domain = None, ignore_devices = False, ignore = []):
+ """ Get SXP representation of this config object.
+
+ @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() != None:
+ sxpr.append(['domid', domain.getDomid()])
+
+ for cfg, typefunc in ROUNDTRIPPING_CONFIG_ENTRIES:
+ if cfg in self:
+ if self[cfg] != None:
+ sxpr.append([cfg, self[cfg]])
+
+ if 'image' in self and self['image'] != None:
+ sxpr.append(['image', self['image']])
+ if 'security' in self and self['security']:
+ sxpr.append(['security', self['security']])
+ if 'shutdown_reason' in self:
+ sxpr.append(['shutdown_reason', self['shutdown_reason']])
+ if 'cpu_time' in self:
+ sxpr.append(['cpu_time', self['cpu_time']/1e9])
+
+ sxpr.append(['online_vcpus', self['online_vcpus']])
+
+ if 'start_time' in self:
+ uptime = time.time() - self['start_time']
+ sxpr.append(['up_time', str(uptime)])
+ sxpr.append(['start_time', str(self['start_time'])])
+
+ if domain:
+ sxpr.append(['status', str(domain.state)])
+ else:
+ sxpr.append(['status', str(DOM_STATE_HALTED)])
+
+ sxpr.append(['state', self._get_old_state_string()])
+ sxpr.append(['memory_dynamic_max', self.get('memory_dynamic_max',
+ self['memory'])])
+
+ # For save/restore migration
+ 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:
+ for cls in XendDevices.valid_devices():
+ found = False
+
+ # figure if there is a device that is running
+ if domain:
+ try:
+ controller = domain.getDeviceController(cls)
+ configs = controller.configurations()
+ for config in configs:
+ 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])
+
+ return sxpr
+
+ def validate(self):
+ """ Validate the configuration and fill in missing configuration
+ with defaults.
+ """
+
+ # Fill in default values
+ for key, default_func in DEFAULT_CONFIGURATION:
+ if key not in self or self[key] == None:
+ self[key] = default_func(self)
+
+ # Basic sanity checks
+ if 'image' in self and isinstance(self['image'], str):
+ self['image'] = sxp.from_string(self['image'])
+ if 'security' in self and isinstance(self['security'], str):
+ self['security'] = sxp.from_string(self['security'])
+ if self['memory'] == 0 and 'mem_kb' in self:
+ self['memory'] = (self['mem_kb'] + 1023)/1024
+ if self['memory'] <= 0:
+ raise XendConfigError('Invalid memory size: %s' %
+ str(self['memory']))
+
+ self['maxmem'] = max(self['memory'], self['maxmem'])
+
+ # convert mem_kb from domain_getinfo to something more descriptive
+ if 'mem_kb' in self:
+ self['memory_dynamic_max'] = (self['mem_kb'] + 1023)/1024
+
+ # Verify devices
+ for d_uuid, (d_type, d_info) in self['device'].items():
+ if d_type not in XendDevices.valid_devices():
+ raise XendConfigError('Invalid device (%s)' % d_type)
+
+ # Verify restart modes
+ for event in ('on_poweroff', 'on_reboot', 'on_crash'):
+ if self[event] not in CONFIG_RESTART_MODES:
+ raise XendConfigError('Invalid restart event: %s = %s' % \
+ (event, str(self[event])))
+
+ # Verify that {vif,vbd}_refs are here too
+ if 'vif_refs' not in self:
+ self['vif_refs'] = []
+ if 'vbd_refs' not in self:
+ self['vbd_refs'] = []
+ if 'vtpm_refs' not in self:
+ self['vtpm_refs'] = []
+
+ def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None):
+ 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:
+ dev_info = {}
+
+ try:
+ for opt, val in cfg_sxp[1:]:
+ dev_info[opt] = val
+ except ValueError:
+ pass # SXP has no options for this device
+
+ # create uuid if it doesn't exist
+ dev_uuid = dev_info.get('uuid', uuid.createString())
+ dev_info['uuid'] = dev_uuid
+ self['device'][dev_uuid] = (dev_type, dev_info)
+ if dev_type in ('vif', 'vbd'):
+ self['%s_refs' % dev_type].append(dev_uuid)
+ elif dev_type in ('tap',):
+ self['vbd_refs'].append(dev_uuid)
+ return dev_uuid
+
+ if cfg_xenapi:
+ dev_info = {}
+ if dev_type == 'vif':
+ if cfg_xenapi.get('MAC'): # don't add if blank
+ dev_info['mac'] = cfg_xenapi.get('MAC')
+ # 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')
+
+ dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
+ dev_info['uuid'] = dev_uuid
+ self['device'][dev_uuid] = (dev_type, dev_info)
+ self['vif_refs'].append(dev_uuid)
+ return dev_uuid
+
+ elif dev_type == 'vbd':
+ dev_info['uname'] = cfg_xenapi.get('image', None)
+ dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
+ if cfg_xenapi.get('mode') == 'RW':
+ dev_info['mode'] = 'w'
+ else:
+ dev_info['mode'] = 'r'
+
+ dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
+ dev_info['uuid'] = dev_uuid
+ self['device'][dev_uuid] = (dev_type, dev_info)
+ self['vbd_refs'].append(dev_uuid)
+ return dev_uuid
+
+ elif dev_type == 'vtpm':
+ if cfg_xenapi.get('type'):
+ dev_info['type'] = cfg_xenapi.get('type')
+ dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
+ dev_info['uuid'] = dev_uuid
+ self['device'][dev_uuid] = (dev_type, dev_info)
+ self['vtpm_refs'].append(dev_uuid)
+ return dev_uuid
+
+ elif dev_type == 'tap':
+ dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
+ dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
+
+ if cfg_xenapi.get('mode') == 'RW':
+ dev_info['mode'] = 'w'
+ else:
+ dev_info['mode'] = 'r'
+
+ dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
+ dev_info['uuid'] = dev_uuid
+ self['device'][dev_uuid] = (dev_type, dev_info)
+ self['vbd_refs'].append(dev_uuid)
+ return dev_uuid
+
+ return ''
+
+ def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None):
+ """Get Device SXPR by either giving the device UUID or (type, config).
+
+ @rtype: list of lists
+ @return: device config sxpr
+ """
+ sxpr = []
+ if dev_uuid != None and dev_uuid in self['device']:
+ dev_type, dev_info = self['device'][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)
+ config = [(opt, val) for opt, val in dev_info.items()]
+ sxpr += config
+
+ return sxpr
+
+ def all_devices_sxpr(self):
+ sxprs = []
+ for dev_type, dev_info in self['device'].values():
+ sxpr = self.device_sxpr(dev_type = dev_type, dev_info = dev_info)
+ sxprs.append((dev_type, sxpr))
+ return sxprs
+
+
+#
+# debugging
+#
+
+if __name__ == "__main__":
+ pass
+
diff --git a/tools/python/xen/xend/XendConstants.py b/tools/python/xen/xend/XendConstants.py
new file mode 100644
index 0000000000..d956c2fe4a
--- /dev/null
+++ b/tools/python/xen/xend/XendConstants.py
@@ -0,0 +1,96 @@
+#============================================================================
+# 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"
+}
+
+restart_modes = [
+ "restart",
+ "destroy",
+ "preserve",
+ "rename-restart"
+ ]
+
+DOM_STATES = [
+ 'halted',
+ 'paused',
+ 'running',
+ 'suspended',
+ 'shutdown',
+ '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_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 = 30.0
+
+ZOMBIE_PREFIX = 'Zombie-'
+
+"""Minimum time between domain restarts in seconds."""
+MINIMUM_RESTART_TIME = 20
+
+RESTART_IN_PROGRESS = 'xend/restart_in_progress'
+
+#
+# Device migration stages (eg. XendDomainInfo, XendCheckpoint, server.tpmif)
+#
+
+DEV_MIGRATE_TEST = 0
+DEV_MIGRATE_STEP1 = 1
+DEV_MIGRATE_STEP2 = 2
+DEV_MIGRATE_STEP3 = 3
+
+#
+# Xenstore Constants
+#
+
+XS_VMROOT = "/vm/"
+
diff --git a/tools/python/xen/xend/XendDevices.py b/tools/python/xen/xend/XendDevices.py
new file mode 100644
index 0000000000..4125cee2ae
--- /dev/null
+++ b/tools/python/xen/xend/XendDevices.py
@@ -0,0 +1,68 @@
+#===========================================================================
+# 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, usbif
+from xen.xend.server.BlktapController import BlktapController
+
+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,
+ 'usb': usbif.UsbifController,
+ 'tap': BlktapController,
+ }
+
+ #@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)
+
diff --git a/tools/python/xen/xend/XendDomain.py b/tools/python/xen/xend/XendDomain.py
index 15a3934b12..d24bf75070 100644
--- a/tools/python/xen/xend/XendDomain.py
+++ b/tools/python/xen/xend/XendDomain.py
@@ -22,45 +22,60 @@
Needs to be persistent for one uptime.
"""
-import logging
import os
+import shutil
import socket
-import sys
import threading
import xen.lowlevel.xc
-import XendDomainInfo
-from xen.xend import XendRoot
-from xen.xend import XendCheckpoint
+from xen.xend import XendRoot, XendCheckpoint, XendDomainInfo
+from xen.xend.PrettyPrint import prettyprint
+from xen.xend.XendConfig import XendConfig
from xen.xend.XendError import XendError, XendInvalidDomain
from xen.xend.XendLogging import log
+from xen.xend.XendConstants import XS_VMROOT
+from xen.xend.XendConstants import DOM_STATE_HALTED, DOM_STATE_RUNNING
+
from xen.xend.xenstore.xstransact import xstransact
from xen.xend.xenstore.xswatch import xswatch
from xen.util import security
-
+from xen.xend import uuid
xc = xen.lowlevel.xc.xc()
-xroot = XendRoot.instance()
-
+xroot = XendRoot.instance()
__all__ = [ "XendDomain" ]
-PRIV_DOMAIN = 0
-VMROOT = '/vm/'
-
+CACHED_CONFIG_FILE = 'config.sxp'
+CHECK_POINT_FILE = 'checkpoint.chk'
+DOM0_UUID = "00000000-0000-0000-0000-000000000000"
+DOM0_NAME = "Domain-0"
+DOM0_ID = 0
class XendDomain:
"""Index of all domains. Singleton.
- """
- ## public:
+ @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
@@ -68,85 +83,277 @@ class XendDomain:
# instance() must be able to return a valid instance of this class even
# during this initialisation.
def init(self):
- xstransact.Mkdir(VMROOT)
- xstransact.SetPermissions(VMROOT, { 'dom' : PRIV_DOMAIN })
+ """Singleton initialisation function."""
+
+ dom_path = self._managed_path()
+ try:
+ os.stat(dom_path)
+ except OSError:
+ log.info("Making %s", dom_path)
+ os.makedirs(dom_path, 0755)
+
+ xstransact.Mkdir(XS_VMROOT)
+ xstransact.SetPermissions(XS_VMROOT, {'dom': DOM0_ID})
self.domains_lock.acquire()
try:
- self._add_domain(
- XendDomainInfo.recreate(self.xen_domains()[PRIV_DOMAIN],
- True))
- self.dom0_setup()
+ 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)
+ self._add_domain(dom0)
+ 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.onChangeDomain)
- xswatch("@releaseDomain", self.onChangeDomain)
-
- self.refresh(True)
+ 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.
- def list(self):
- """Get list of domain objects.
+ Calls refresh which will keep the local cache of domains
+ in sync.
- @return: domain objects
+ @rtype: int
+ @return: 1
"""
self.domains_lock.acquire()
try:
- self.refresh()
- return self.domains.values()
+ 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.
- def list_sorted(self):
- """Get list of domain objects, sorted by name.
+ Note: L{XendDomainInfo._checkName} will call back into XendDomain
+ to make sure domain name is not a duplicate.
- @return: domain objects
"""
- doms = self.list()
- doms.sort(lambda x, y: cmp(x.getName(), y.getName()))
- return doms
+ self.domains_lock.acquire()
+ try:
+ running = self._running_domains()
+ managed = self._managed_domains()
- def list_names(self):
- """Get list of domain names.
+ # add all active domains
+ for dom in running:
+ if dom['dying'] == 1:
+ log.warn('Ignoring dying domain %d from now on' %
+ dom['domid'])
+ continue
- @return: domain names
- """
- doms = self.list_sorted()
- return map(lambda x: x.getName(), doms)
+ if dom['domid'] != DOM0_ID:
+ try:
+ new_dom = XendDomainInfo.recreate(dom, False)
+ self._add_domain(new_dom)
+ except Exception:
+ log.exception("Failed to create reference to running "
+ "domain id: %d" % dom['domid'])
+
+ # 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', '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)
+ except Exception:
+ log.exception("Failed to create reference to managed "
+ "domain: %s" % dom_name)
+ finally:
+ self.domains_lock.release()
- ## private:
- def onChangeDomain(self, _):
- self.domains_lock.acquire()
+ # -----------------------------------------------------------------
+ # 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 = xroot.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:
- self.refresh()
- finally:
- self.domains_lock.release()
- return 1
+ 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)
+
+ # make sure the domain dir exists
+ if not os.path.exists(domains_dir):
+ os.makedirs(domains_dir, 0755)
+ elif not os.path.isdir(domains_dir):
+ log.error("xend_domain_dir is not a directory.")
+ raise XendError("Unable to save managed configuration "
+ "because %s is not a directory." %
+ domains_dir)
+
+ if not os.path.exists(domain_config_dir):
+ try:
+ os.makedirs(domain_config_dir, 0755)
+ except IOError:
+ log.exception("Failed to create directory: %s" %
+ domain_config_dir)
+ raise XendError("Failed to create directory: %s" %
+ domain_config_dir)
+
+ try:
+ sxp_cache_file = open(self._managed_config_path(dom_uuid),'w')
+ prettyprint(dominfo.sxpr(), sxp_cache_file, width = 78)
+ sxp_cache_file.close()
+ except IOError:
+ log.error("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.
- def xen_domains(self):
- """Get table of domains indexed by id from xc. Expects to be
- protected by the domains_lock.
+ @rtype: list of XendConfig
+ @return: List of domain configurations that are managed.
"""
- domlist = xc.domain_getinfo()
- doms = {}
- for d in domlist:
- domid = d['dom']
- doms[domid] = d
+ 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(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()]
+ 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{XendRoot}.
- def dom0_setup(self):
- """Expects to be protected by the domains_lock."""
- dom0 = self.domains[PRIV_DOMAIN]
+ @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(xroot.get_dom0_vcpus())
@@ -157,264 +364,655 @@ class XendDomain:
dom0.setVCpuCount(target)
- def _add_domain(self, info):
- """Add the given domain entry to this instance's internal cache.
+ def _refresh(self):
+ """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
"""
- self.domains[info.getDomid()] = info
+ # update information for all running domains
+ # - like cpu_time, status, dying, etc.
+ running = self._running_domains()
+ for dom in running:
+ domid = dom['domid']
+ if domid in self.domains and dom['dying'] != 1:
+ self.domains[domid].update(dom)
- def _delete_domain(self, domid):
- """Remove the given domain from this instance's internal cache.
- Expects to be protected by the domains_lock.
+ # 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_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
"""
- info = self.domains.get(domid)
+ log.debug("Adding Domain: %s" % info.getDomid())
+ self.domains[info.getDomid()] = info
+
+ 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:
- del self.domains[domid]
- info.cleanupDomain()
-
-
- def refresh(self, initialising = False):
- """Refresh domain list from Xen. Expects to be protected by the
- domains_lock.
-
- @param initialising True if this is the first refresh after starting
- Xend. This does not change this method's behaviour, except for
- logging.
- """
- doms = self.xen_domains()
- for d in self.domains.values():
- info = doms.get(d.getDomid())
- if info:
- d.update(info)
- else:
- self._delete_domain(d.getDomid())
- for d in doms:
- if d not in self.domains:
- if doms[d]['dying']:
- log.log(initialising and logging.ERROR or logging.DEBUG,
- 'Cannot recreate information for dying domain %d.'
- ' Xend will ignore this domain from now on.',
- doms[d]['dom'])
- elif d == PRIV_DOMAIN:
- log.fatal(
- "No record of privileged domain %d! Terminating.", d)
- sys.exit(1)
- else:
- try:
- self._add_domain(
- XendDomainInfo.recreate(doms[d], False))
- except:
- log.exception(
- "Failed to recreate information for domain "
- "%d. Destroying it in the hope of "
- "recovery.", d)
- try:
- xc.domain_destroy(d)
- except:
- log.exception('Destruction of %d failed.', d)
+ if domid == None:
+ domid = info.getDomid()
+ if info.state != DOM_STATE_HALTED:
+ info.cleanupDomain()
+
+ if domid in self.domains:
+ del self.domains[domid]
+ else:
+ log.warning("Attempted to remove non-existent domain.")
- ## public:
+ def restore_(self, config):
+ """Create a domain as part of the restore process. This is called
+ only from L{XendCheckpoint}.
- def domain_create(self, config):
- """Create a domain from a configuration.
+ 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
- @return: domain
+ @param config: Configuration of domain to restore
+ @type config: SXP Object (eg. list of lists)
"""
self.domains_lock.acquire()
try:
- dominfo = XendDomainInfo.create(config)
+ security.refresh_ssidref(config)
+ dominfo = XendDomainInfo.restore(config)
self._add_domain(dominfo)
- self.domain_sched_credit_set(dominfo.getDomid(),
- dominfo.getWeight(),
- dominfo.getCap())
return dominfo
finally:
self.domains_lock.release()
- def domain_configure(self, config):
- """Configure an existing domain.
+ 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 XendError: If domain is not found.
+ """
+ self.domains_lock.acquire()
+ try:
+ self._refresh()
+ dom = self.domain_lookup_nr(domid)
+ if not dom:
+ raise XendError("No domain named '%s'." % str(domid))
+ return dom
+ finally:
+ self.domains_lock.release()
- @param vmconfig: vm configuration
+
+ 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
"""
- # !!!
- raise XendError("Unsupported")
+ 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
- def domain_restore(self, src):
- """Restore a domain from file.
+ # 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]
- @param src: source file
+ 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 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:
- fd = os.open(src, os.O_RDONLY)
- try:
- return self.domain_restore_fd(fd)
- finally:
- os.close(fd)
- except OSError, ex:
- raise XendError("can't read guest state file %s: %s" %
- (src, ex[1]))
+ for dom in self.domains.values():
+ if dom.getName() == DOM0_NAME:
+ continue
+
+ if dom.state == 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':
+ chkfile = self._managed_check_point_path(dom.getName())
+ self.domain_save(dom.domid, chkfile)
+ finally:
+ self.domains_lock.release()
- def domain_restore_fd(self, fd):
- """Restore a domain from the given file descriptor."""
+
+ # ----------------------------------------------------------------
+ # 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:
- return XendCheckpoint.restore(self, fd)
- 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")
+ self.domains_lock.acquire()
+ result = [d.get_uuid() for d in self.domains.values()]
+ result += self.managed_domains.keys()
+ 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
- def restore_(self, config):
- """Create a domain as part of the restore process. This is called
- only from {@link XendCheckpoint}.
+ if vm_uuid in self.managed_domains:
+ return self.managed_domains[vm_uuid]
- A restore request comes into XendDomain through {@link
- #domain_restore} or {@link #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 {@link 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.
- """
+ return None
+ finally:
+ self.domains_lock.release()
+
+ def get_vm_with_dev_uuid(self, klass, dev_uuid):
self.domains_lock.acquire()
try:
- security.refresh_ssidref(config)
- dominfo = XendDomainInfo.restore(config)
- self._add_domain(dominfo)
- return dominfo
+ 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):
+ self.domains_lock.acquire()
+ try:
+ dom = self.get_vm_with_dev_uuid(klass, dev_uuid)
+ if not dom:
+ return None
- def domain_lookup(self, domid):
+ value = dom.get_device_property(klass, dev_uuid, field)
+ return value
+ except ValueError, e:
+ pass
+
+ return None
+
+ 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):
self.domains_lock.acquire()
try:
- self.refresh()
- return self.domains.get(domid)
+ for domid, dom in self.domains.items():
+ if dom.get_uuid == vm_uuid:
+ return fn(domid, *args)
+
+ if vm_uuid in self.managed_domains:
+ domid = self.managed_domains[vm_uuid].getDomid()
+ if domid == None:
+ domid = self.managed_domains[vm_uuid].getName()
+ return fn(domid, *args)
+
+ raise XendInvalidDomain("Domain does not exist")
finally:
self.domains_lock.release()
+
+ def create_domain(self, xenapi_vm):
+ self.domains_lock.acquire()
+ try:
+ try:
+ xeninfo = XendConfig(xenapi_vm = 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 domain_lookup_nr(self, domid):
+ def rename_domain(self, dom, new_name):
self.domains_lock.acquire()
try:
- return self.domains.get(domid)
+ old_name = dom.getName()
+ dom.setName(new_name)
+
finally:
self.domains_lock.release()
+
+
+ #
+ # End of Xen API
+ # ----------------------------------------------------------------
+ # ------------------------------------------------------------
+ # Xen Legacy API
- def domain_lookup_by_name_or_id(self, name):
+ def list(self):
+ """Get list of domain objects.
+
+ @return: domains
+ @rtype: list of XendDomainInfo
+ """
self.domains_lock.acquire()
try:
- self.refresh()
- return self.domain_lookup_by_name_or_id_nr(name)
+ self._refresh()
+
+ # 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)
+
+ return active_domains + inactive_domains
finally:
self.domains_lock.release()
- def domain_lookup_by_name_or_id_nr(self, name):
+ def list_sorted(self):
+ """Get list of domain objects, sorted by name.
+
+ @return: domain objects
+ @rtype: list of XendDomainInfo
+ """
+ doms = self.list()
+ doms.sort(lambda x, y: cmp(x.getName(), y.getName()))
+ return doms
+
+ def list_names(self):
+ """Get list of domain names.
+
+ @return: domain names
+ @rtype: list of strings.
+ """
+ return [d.getName() for d in self.list_sorted()]
+
+ 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 save privileged domain %s" % domname)
+
+ if dominfo.state != DOM_STATE_RUNNING:
+ raise XendError("Cannot suspend domain that is not running.")
+
+ if not os.path.exists(self._managed_config_path(domname)):
+ raise XendError("Domain is not managed by Xend lifecycle " +
+ "support.")
+
+ path = self._managed_check_point_path(domname)
+ fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
+ 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):
+ """Resumes a domain that is persistently managed by Xend.
+
+ @param domname: Domain Name
+ @type domname: string
+ @rtype: None
+ @raise XendError: If failed to restore.
+ """
+ try:
+ dominfo = self.domain_lookup_nr(domname)
+
+ if not dominfo:
+ raise XendInvalidDomain(domname)
+
+ if dominfo.getDomid() == DOM0_ID:
+ raise XendError("Cannot save privileged domain %s" % domname)
+
+ if dominfo.state != DOM_STATE_HALTED:
+ raise XendError("Cannot suspend domain that is not running.")
+
+ chkpath = self._managed_check_point_path(domname)
+ 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.state)
+ XendCheckpoint.restore(self,
+ os.open(chkpath, os.O_RDONLY),
+ dominfo)
+ 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))
+
+
+ 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:
- dominfo = self.domain_lookup_by_name_nr(name)
+ self._refresh()
- if dominfo:
- return dominfo
- else:
- try:
- return self.domains.get(int(name))
- except ValueError:
- return None
+ dominfo = XendDomainInfo.create(config)
+ self._add_domain(dominfo)
+ self.domain_sched_credit_set(dominfo.getDomid(),
+ dominfo.getWeight(),
+ dominfo.getCap())
+ return dominfo
finally:
self.domains_lock.release()
- def domain_lookup_by_name_nr(self, name):
+ 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:
- matching = filter(lambda d: d.getName() == name,
- self.domains.values())
- n = len(matching)
- if n == 1:
- return matching[0]
- return None
+ try:
+ xeninfo = XendConfig(sxp = config)
+ dominfo = XendDomainInfo.createDormant(xeninfo)
+ 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 a managed domain
- def privilegedDomain(self):
+ @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:
- return self.domains[PRIV_DOMAIN]
+ self._refresh()
+
+ dominfo = self.domain_lookup_nr(domid)
+ if not dominfo:
+ raise XendInvalidDomain(str(domid))
+
+ if dominfo.state != DOM_STATE_HALTED:
+ raise XendError("Domain is already running")
+
+ dominfo.start(is_managed = True)
+ self._add_domain(dominfo)
finally:
self.domains_lock.release()
+
-
- def domain_unpause(self, domid):
- """Unpause domain execution."""
+ def domain_delete(self, domid):
+ """Remove a managed domain from database
- dominfo = self.domain_lookup_by_name_or_id_nr(domid)
- if not dominfo:
- raise XendInvalidDomain(str(domid))
+ @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.getDomid() == PRIV_DOMAIN:
- raise XendError("Cannot unpause privileged domain %s" % domid)
+ if dominfo.state != DOM_STATE_HALTED:
+ raise XendError("Domain is still running")
+ self._managed_domain_unregister(dominfo)
+ self._remove_domain(dominfo)
+
+ except Exception, ex:
+ raise XendError(str(ex))
+ finally:
+ self.domains_lock.release()
+
+
+ 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):
+ """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:
- log.info("Domain %s (%d) unpaused.", dominfo.getName(),
- dominfo.getDomid())
- return dominfo.unpause()
- except Exception, ex:
- raise XendError(str(ex))
+ fd = os.open(src, os.O_RDONLY)
+ try:
+ return self.domain_restore_fd(fd)
+ 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):
+ """Restore a domain from the given file descriptor.
- def domain_pause(self, domid):
- """Pause domain execution."""
+ @param fd: file descriptor of the checkpoint file
+ @type fd: File object
+ @rtype: XendDomainInfo
+ @raise XendError: if failed to restore
+ """
- dominfo = self.domain_lookup_by_name_or_id_nr(domid)
- if not dominfo:
- raise XendInvalidDomain(str(domid))
+ try:
+ return XendCheckpoint.restore(self, fd)
+ 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.
- if dominfo.getDomid() == PRIV_DOMAIN:
- raise XendError("Cannot pause privileged domain %s" % domid)
+ @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))
+
+ 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):
+ """Pause domain execution.
+
+ @param domid: Domain ID or Name
+ @type domid: int or string.
+ @rtype: None
+ @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))
log.info("Domain %s (%d) paused.", dominfo.getName(),
- dominfo.getDomid())
- return dominfo.pause()
+ int(dominfo.getDomid()))
+ dominfo.pause()
+ 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_by_name_or_id_nr(domid)
+ dominfo = self.domain_lookup_nr(domid)
if not dominfo:
raise XendInvalidDomain(str(domid))
- if dominfo.getDomid() == PRIV_DOMAIN:
+ if dominfo.getDomid() == DOM0_ID:
raise XendError("Cannot dump core for privileged domain %s" % domid)
try:
- log.info("Domain core dump requested for domain %s (%d) live=%d crash=%d.",
+ 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."""
+ """Terminate domain immediately.
- dominfo = self.domain_lookup_by_name_or_id_nr(domid)
- if dominfo and dominfo.getDomid() == PRIV_DOMAIN:
+ @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:
@@ -422,19 +1020,36 @@ class XendDomain:
else:
try:
val = xc.domain_destroy(int(domid))
- except Exception, ex:
- raise XendInvalidDomain(str(domid))
+ except ValueError:
+ raise XendInvalidDomain(domid)
+ except Exception, e:
+ raise XendError(str(e))
+
return val
def domain_migrate(self, domid, dst, live=False, resource=0, port=0):
- """Start domain migration."""
+ """Start domain migration.
+
+ @param domid: Domain ID or Name
+ @type domid: int or string.
+ @param dst: Destination IP address
+ @type dst: string
+ @keyword port: relocation port on destination
+ @type port: int
+ @keyword live: Live migration
+ @type live: bool
+ @keyword resource: not used??
+ @rtype: None
+ @raise XendError: Failed to migrate
+ @raise XendInvalidDomain: Domain is not valid
+ """
- dominfo = self.domain_lookup_by_name_or_id_nr(domid)
+ dominfo = self.domain_lookup_nr(domid)
if not dominfo:
raise XendInvalidDomain(str(domid))
- if dominfo.getDomid() == PRIV_DOMAIN:
- raise XendError("Cannot migrate privileged domain %s" % domid)
+ if dominfo.getDomid() == DOM0_ID:
+ raise XendError("Cannot migrate privileged domain %i" % domid)
""" The following call may raise a XendError exception """
dominfo.testMigrateDevices(True, dst)
@@ -460,21 +1075,26 @@ class XendDomain:
def domain_save(self, domid, dst):
"""Start saving a domain to file.
- @param dst: destination 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_by_name_or_id_nr(domid)
+ dominfo = self.domain_lookup_nr(domid)
if not dominfo:
raise XendInvalidDomain(str(domid))
- if dominfo.getDomid() == PRIV_DOMAIN:
- raise XendError("Cannot save privileged domain %s" % domid)
+ if dominfo.getDomid() == DOM0_ID:
+ raise XendError("Cannot save privileged domain %i" % domid)
fd = os.open(dst, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
try:
# For now we don't support 'live checkpoint'
- return XendCheckpoint.save(fd, dominfo, False, False, dst)
+ XendCheckpoint.save(fd, dominfo, False, False, dst)
finally:
os.close(fd)
except OSError, ex:
@@ -484,9 +1104,15 @@ class XendDomain:
def domain_pincpu(self, domid, vcpu, cpumap):
"""Set which cpus vcpu can use
- @param cpumap: string repr of list of usable cpus
+ @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_by_name_or_id_nr(domid)
+ dominfo = self.domain_lookup_nr(domid)
if not dominfo:
raise XendInvalidDomain(str(domid))
@@ -507,8 +1133,12 @@ class XendDomain:
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_by_name_or_id_nr(domid)
+ dominfo = self.domain_lookup_nr(domid)
if not dominfo:
raise XendInvalidDomain(str(domid))
try:
@@ -519,15 +1149,20 @@ class XendDomain:
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_by_name_or_id_nr(domid)
+ 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',
- ['domain', sedf_info['domain']],
+ ['domid', sedf_info['domid']],
['period', sedf_info['period']],
['slice', sedf_info['slice']],
['latency', sedf_info['latency']],
@@ -538,7 +1173,14 @@ class XendDomain:
raise XendError(str(ex))
def domain_shadow_control(self, domid, op):
- """Shadow page control."""
+ """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)
@@ -546,7 +1188,13 @@ class XendDomain:
raise XendError(str(ex))
def domain_shadow_mem_get(self, domid):
- """Get shadow pagetable memory allocation."""
+ """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())
@@ -554,7 +1202,15 @@ class XendDomain:
raise XendError(str(ex))
def domain_shadow_mem_set(self, domid, mb):
- """Set shadow pagetable memory allocation."""
+ """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)
@@ -563,8 +1219,13 @@ class XendDomain:
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_by_name_or_id_nr(domid)
+ dominfo = self.domain_lookup_nr(domid)
if not dominfo:
raise XendInvalidDomain(str(domid))
try:
@@ -574,8 +1235,14 @@ class XendDomain:
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
"""
- dominfo = self.domain_lookup_by_name_or_id_nr(domid)
+ dominfo = self.domain_lookup_nr(domid)
if not dominfo:
raise XendInvalidDomain(str(domid))
try:
@@ -589,17 +1256,25 @@ class XendDomain:
elif cap < 0 or cap > dominfo.getVCpuCount() * 100:
raise XendError("cap is out of range")
+ assert type(weight) == int
+ assert type(cap) == int
+
return xc.sched_credit_domain_set(dominfo.getDomid(), weight, cap)
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)
- @return: 0 on success, -1 on error
+ @type mem: int
+ @raise XendError: fail to set memory
+ @rtype: 0
"""
- dominfo = self.domain_lookup_by_name_or_id_nr(domid)
+ dominfo = self.domain_lookup_nr(domid)
if not dominfo:
raise XendInvalidDomain(str(domid))
maxmem = int(mem) * 1024
@@ -613,9 +1288,10 @@ class XendDomain:
@param first: first IO port
@param last: last IO port
- @return: 0 on success, -1 on error
+ @raise XendError: failed to set range
+ @rtype: 0
"""
- dominfo = self.domain_lookup_by_name_or_id_nr(domid)
+ dominfo = self.domain_lookup_nr(domid)
if not dominfo:
raise XendInvalidDomain(str(domid))
nr_ports = last - first + 1
@@ -632,9 +1308,10 @@ class XendDomain:
@param first: first IO port
@param last: last IO port
- @return: 0 on success, -1 on error
+ @raise XendError: failed to set range
+ @rtype: 0
"""
- dominfo = self.domain_lookup_by_name_or_id_nr(domid)
+ dominfo = self.domain_lookup_nr(domid)
if not dominfo:
raise XendInvalidDomain(str(domid))
nr_ports = last - first + 1
diff --git a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py
index a469b93a06..6bfa401bb7 100644
--- a/tools/python/xen/xend/XendDomainInfo.py
+++ b/tools/python/xen/xend/XendDomainInfo.py
@@ -24,91 +24,40 @@ Author: Mike Wray <mike.wray@hp.com>
"""
-import errno
import logging
-import string
import time
import threading
+import re
+import copy
import os
+from types import StringTypes
import xen.lowlevel.xc
from xen.util import asserts
from xen.util.blkif import blkdev_uname_to_file
from xen.util import security
-import balloon
-import image
-import sxp
-import uuid
-import XendDomain
-import XendRoot
+
+from xen.xend import balloon, sxp, uuid, image, arch
+from xen.xend import XendRoot, XendNode
from xen.xend.XendBootloader import bootloader
+from xen.xend.XendConfig import XendConfig
from xen.xend.XendError import XendError, VmError
-
+from xen.xend.XendDevices import XendDevices
from xen.xend.xenstore.xstransact import xstransact, complete
from xen.xend.xenstore.xsutil import GetDomainPath, IntroduceDomain
from xen.xend.xenstore.xswatch import xswatch
+from xen.xend.XendConstants import *
+from xen.xend.XendAPIConstants import *
-from xen.xend import arch
-
-"""Shutdown code for poweroff."""
-DOMAIN_POWEROFF = 0
-
-"""Shutdown code for reboot."""
-DOMAIN_REBOOT = 1
-
-"""Shutdown code for suspend."""
-DOMAIN_SUSPEND = 2
-
-"""Shutdown code for crash."""
-DOMAIN_CRASH = 3
-
-"""Shutdown code for halt."""
-DOMAIN_HALT = 4
-
-"""Map shutdown codes to strings."""
-shutdown_reasons = {
- DOMAIN_POWEROFF: "poweroff",
- DOMAIN_REBOOT : "reboot",
- DOMAIN_SUSPEND : "suspend",
- DOMAIN_CRASH : "crash",
- DOMAIN_HALT : "halt"
- }
-
-restart_modes = [
- "restart",
- "destroy",
- "preserve",
- "rename-restart"
- ]
-
-STATE_DOM_OK = 1
-STATE_DOM_SHUTDOWN = 2
-
-SHUTDOWN_TIMEOUT = 30.0
MIGRATE_TIMEOUT = 30.0
-ZOMBIE_PREFIX = 'Zombie-'
-
-"""Constants for the different stages of ext. device migration """
-DEV_MIGRATE_TEST = 0
-DEV_MIGRATE_STEP1 = 1
-DEV_MIGRATE_STEP2 = 2
-DEV_MIGRATE_STEP3 = 3
-
-"""Minimum time between domain restarts in seconds."""
-MINIMUM_RESTART_TIME = 20
-
-RESTART_IN_PROGRESS = 'xend/restart_in_progress'
-
-
xc = xen.lowlevel.xc.xc()
xroot = XendRoot.instance()
log = logging.getLogger("xend.XendDomainInfo")
#log.setLevel(logging.TRACE)
-
##
# All parameters of VMs that may be configured on-the-fly, or at start-up.
#
@@ -157,6 +106,8 @@ VM_STORE_ENTRIES = [
('shadow_memory', int),
('maxmem', int),
('start_time', float),
+ ('on_xend_start', str),
+ ('on_xend_stop', str),
]
VM_STORE_ENTRIES += VM_CONFIG_PARAMS
@@ -182,77 +133,102 @@ VM_STORE_ENTRIES += VM_CONFIG_PARAMS
def create(config):
- """Create a VM from a configuration.
+ """Creates and start a VM using the supplied configuration.
+ (called from XMLRPCServer directly)
- @param config configuration
- @raise: VmError for invalid configuration
+ @param config: A configuration object involving lists of tuples.
+ @type config: list of lists, eg ['vm', ['image', 'xen.gz']]
+
+ @rtype: XendDomainInfo
+ @return: A up and running XendDomainInfo instance
+ @raise VmError: Invalid configuration or failure to start.
"""
log.debug("XendDomainInfo.create(%s)", config)
-
- vm = XendDomainInfo(parseConfig(config))
+ vm = XendDomainInfo(XendConfig(sxp = config))
try:
- vm.construct()
- vm.initDomain()
- vm.storeVmDetails()
- vm.storeDomDetails()
- vm.registerWatches()
- vm.refreshShutdown()
- return vm
+ vm.start()
except:
log.exception('Domain construction failed')
vm.destroy()
raise
+ return vm
-def recreate(xeninfo, priv):
+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."""
+ and asking us to recreate them causes problems.
+
+ @param xeninfo: Parsed configuration
+ @type xeninfo: Dictionary
+ @param priv: TODO, unknown, something to do with memory
+ @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)", xeninfo)
+ log.debug("XendDomainInfo.recreate(%s)", info)
- assert not xeninfo['dying']
+ assert not info['dying']
- domid = xeninfo['dom']
+ xeninfo = XendConfig(cfg = info)
+ domid = xeninfo['domid']
uuid1 = xeninfo['handle']
xeninfo['uuid'] = uuid.toString(uuid1)
+ 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.", domid, xeninfo['uuid'])
+ 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:
- raise XendError(
- 'No vm path in store for existing domain %d' % domid)
+ 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:
- raise XendError(
- 'No vm/uuid path in store for existing domain %d' % domid)
-
+ 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:
- raise XendError(
- 'Uuid in store does not match uuid for existing domain %d: '
- '%s != %s' % (domid, uuid2_str, xeninfo['uuid']))
-
- vm = XendDomainInfo(xeninfo, domid, dompath, True, priv)
-
- except Exception, exn:
- if priv:
- log.warn(str(exn))
-
- vm = XendDomainInfo(xeninfo, domid, dompath, True, priv)
- vm.recreateDom()
- vm.removeVm()
- vm.storeVmDetails()
- vm.storeDomDetails()
-
- vm.registerWatches()
+ 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)
+
+ if needs_reinitialising:
+ vm._recreateDom()
+ vm._removeVm()
+ vm._storeVmDetails()
+ vm._storeDomDetails()
+
+ vm._registerWatches()
vm.refreshShutdown(xeninfo)
return vm
@@ -260,146 +236,52 @@ def recreate(xeninfo, priv):
def restore(config):
"""Create a domain and a VM object to do a restore.
- @param config: domain configuration
+ @param config: Domain configuration object
+ @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)", config)
-
- vm = XendDomainInfo(parseConfig(config), None, None, False, False, True)
+ vm = XendDomainInfo(XendConfig(sxp = config), resume = True)
try:
- vm.construct()
- vm.storeVmDetails()
- vm.createDevices()
- vm.createChannels()
- vm.storeDomDetails()
- vm.endRestore()
+ vm.resume()
return vm
except:
vm.destroy()
raise
-
-def parseConfig(config):
- def get_cfg(name, conv = None):
- val = sxp.child_value(config, name)
-
- if conv and not val is None:
- try:
- return conv(val)
- except TypeError, exn:
- raise VmError(
- 'Invalid setting %s = %s in configuration: %s' %
- (name, val, str(exn)))
- else:
- return val
-
-
- log.debug("parseConfig: config is %s", config)
-
- result = {}
-
- for e in ROUNDTRIPPING_CONFIG_ENTRIES:
- result[e[0]] = get_cfg(e[0], e[1])
-
- result['cpu'] = get_cfg('cpu', int)
- result['cpus'] = get_cfg('cpus', str)
- result['image'] = get_cfg('image')
- tmp_security = get_cfg('security')
- if tmp_security:
- result['security'] = tmp_security
-
- try:
- if result['image']:
- v = sxp.child_value(result['image'], 'vcpus')
- if result['vcpus'] is None and v is not None:
- result['vcpus'] = int(v)
- elif v is not None and int(v) != result['vcpus']:
- log.warn(('Image VCPUs setting overrides vcpus=%d elsewhere.'
- ' Using %s VCPUs for VM %s.') %
- (result['vcpus'], v, result['uuid']))
- result['vcpus'] = int(v)
- except TypeError, exn:
- raise VmError(
- 'Invalid configuration setting: vcpus = %s: %s' %
- (sxp.child_value(result['image'], 'vcpus', 1), str(exn)))
-
- try:
- # support legacy config files with 'cpu' parameter
- # NB: prepending to list to support previous behavior
- # where 'cpu' parameter pinned VCPU0.
- if result['cpu']:
- if result['cpus']:
- result['cpus'] = "%s,%s" % (str(result['cpu']), result['cpus'])
- else:
- result['cpus'] = str(result['cpu'])
-
- # convert 'cpus' string to list of ints
- # 'cpus' 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]
- if result['cpus']:
- cpus = []
- for c in result['cpus'].split(','):
- if c.find('-') != -1:
- (x,y) = c.split('-')
- for i in range(int(x),int(y)+1):
- cpus.append(int(i))
- else:
- # remove this element from the list
- if c[0] == '^':
- cpus = [x for x in cpus if x != int(c[1:])]
- else:
- cpus.append(int(c))
-
- result['cpus'] = cpus
-
- except ValueError, exn:
- raise VmError(
- 'Invalid configuration setting: cpus = %s: %s' %
- (result['cpus'], exn))
-
- result['backend'] = []
- for c in sxp.children(config, 'backend'):
- result['backend'].append(sxp.name(sxp.child0(c)))
-
- result['device'] = []
- for d in sxp.children(config, 'device'):
- c = sxp.child0(d)
- result['device'].append((sxp.name(c), c))
-
- # Configuration option "restart" is deprecated. Parse it, but
- # let on_xyz override it if they are present.
- restart = get_cfg('restart')
- if restart:
- def handle_restart(event, val):
- if result[event] is None:
- result[event] = val
-
- if restart == "onreboot":
- handle_restart('on_poweroff', 'destroy')
- handle_restart('on_reboot', 'restart')
- handle_restart('on_crash', 'destroy')
- elif restart == "always":
- handle_restart('on_poweroff', 'restart')
- handle_restart('on_reboot', 'restart')
- handle_restart('on_crash', 'restart')
- elif restart == "never":
- handle_restart('on_poweroff', 'destroy')
- handle_restart('on_reboot', 'destroy')
- handle_restart('on_crash', 'destroy')
- else:
- log.warn("Ignoring malformed and deprecated config option "
- "restart = %s", restart)
-
- result['start_time'] = get_cfg('start_time', float)
-
- log.debug("parseConfig: result is %s", result)
- return result
-
+def createDormant(xeninfo):
+ """Create a dormant/inactive XenDomainInfo without creating VM.
+ This is for creating instances of persistent domains that are not
+ yet start.
+
+ @param xeninfo: Parsed configuration
+ @type xeninfo: dictionary
+
+ @rtype: XendDomainInfo
+ @return: A up and running XendDomainInfo instance
+ @raise XendError: Errors with configuration.
+ """
+
+ log.debug("XendDomainInfo.createDormant(%s)", xeninfo)
+
+ # domid does not make sense for non-running domains.
+ xeninfo.pop('domid', None)
+ vm = XendDomainInfo(XendConfig(cfg = xeninfo))
+ 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)
@@ -411,17 +293,19 @@ def shutdown_reason(code):
@return: shutdown reason
@rtype: string
"""
- return shutdown_reasons.get(code, "?")
+ 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]['dom']:
+ if domlist and dom == domlist[0]['domid']:
return domlist[0]
except Exception, err:
# ignore missing domain
@@ -430,32 +314,87 @@ def dom_get(dom):
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 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):
+ """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 privledged domain (Dom 0) (TODO: really?)
+ @type priv: bool
+ @keyword resume: Is this domain being resumed?
+ @type resume: bool
+ """
self.info = info
-
- if not self.infoIsSet('uuid'):
- self.info['uuid'] = uuid.toString(uuid.create())
-
- if domid is not None:
- self.domid = domid
- elif 'dom' in info:
- self.domid = int(info['dom'])
+ if domid == None:
+ self.domid = self.info.get('domid')
else:
- self.domid = None
-
- self.vmpath = XendDomain.VMROOT + self.info['uuid']
+ self.domid = domid
+
+ #REMOVE: uuid is now generated in XendConfig
+ #if not self._infoIsSet('uuid'):
+ # self.info['uuid'] = uuid.toString(uuid.create())
+
+ #REMOVE: domid logic can be shortened
+ #if domid is not None:
+ # self.domid = domid
+ #elif info.has_key('dom'):
+ # self.domid = int(info['dom'])
+ #else:
+ # self.domid = None
+
+ self.vmpath = XS_VMROOT + self.info['uuid']
self.dompath = dompath
- if augment:
- self.augmentInfo(priv)
-
- self.validateInfo()
-
self.image = None
- self.security = None
self.store_port = None
self.store_mfn = None
self.console_port = None
@@ -463,272 +402,285 @@ class XendDomainInfo:
self.vmWatch = None
self.shutdownWatch = None
-
self.shutdownStartTime = None
- self.state = STATE_DOM_OK
+ self.state = DOM_STATE_HALTED
self.state_updated = threading.Condition()
self.refresh_shutdown_lock = threading.Condition()
+ self._deviceControllers = {}
+
+ for state in DOM_STATES_OLD:
+ self.info[state] = 0
+
+ if augment:
+ self._augmentInfo(priv)
+
+ self._checkName(self.info['name'])
self.setResume(resume)
+
- ## private:
+ #
+ # Public functions available through XMLRPC
+ #
- def readVMDetails(self, params):
- """Read the specified parameters from the store.
+
+ def start(self, is_managed = False):
+ """Attempts to start the VM by do the appropriate
+ initialisation if it not started.
"""
- 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 []
+ from xen.xend import XendDomain
+
+ if self.state == DOM_STATE_HALTED:
+ try:
+ self._constructDomain()
+ self._initDomain()
+ self._storeVmDetails()
+ self._storeDomDetails()
+ self._registerWatches()
+ self.refreshShutdown()
+ self.unpause()
+
+ # save running configuration if XendDomains believe domain is
+ # persistent
+ #
+ if is_managed:
+ xendomains = XendDomain.instance()
+ xendomains.managed_config_save(self)
+ 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."""
+ if self.state in (DOM_STATE_HALTED, DOM_STATE_SUSPENDED):
+ try:
+ self._constructDomain()
+ self._storeVmDetails()
+ self._createDevices()
+ self._createChannels()
+ self._storeDomDetails()
+ self._endRestore()
+ except:
+ log.exception('VM resume failed')
+ raise
+ else:
+ raise XendError('VM already running')
- def storeChanged(self, _):
- log.trace("XendDomainInfo.storeChanged");
+ def shutdown(self, reason):
+ """Shutdown a domain by signalling this via xenstored."""
+ log.debug('XendDomainInfo.shutdown')
+ if self.state in (DOM_STATE_SHUTDOWN, DOM_STATE_HALTED,):
+ raise XendError('Domain cannot be shutdown')
+
+ if not reason in DOMAIN_SHUTDOWN_REASONS.values():
+ raise XendError('Invalid reason: %s' % reason)
+ self._storeDom("control/shutdown", reason)
+
+ 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:
+ raise XendError("Domain unable to be paused: %s" % str(ex))
- changed = False
+ def unpause(self):
+ """Unpause domain
- def f(x, y):
- if y is not None and self.info[x[0]] != y:
- self.info[x[0]] = y
- changed = True
+ @raise XendError: Failed unpausing a domain
+ """
+ try:
+ xc.domain_unpause(self.domid)
+ self._stateSet(DOM_STATE_RUNNING)
+ except Exception, ex:
+ raise XendError("Domain unable to be unpaused: %s" % str(ex))
- map(f, VM_CONFIG_PARAMS, self.readVMDetails(VM_CONFIG_PARAMS))
+ def send_sysrq(self, key):
+ """ Send a Sysrq equivalent key via xenstored."""
+ asserts.isCharConvertible(key)
+ self._storeDom("control/sysrq", '%c' % key)
- im = self.readVm('image')
- current_im = self.info['image']
- if (im is not None and
- (current_im is None or sxp.to_string(current_im) != im)):
- self.info['image'] = sxp.from_string(im)
- changed = True
+ def device_create(self, dev_config):
+ """Create a new device.
- if changed:
- # Update the domain section of the store, as this contains some
- # parameters derived from the VM configuration.
- self.storeDomDetails()
+ @param dev_config: device configuration
+ @type dev_config: dictionary (parsed config)
+ """
+ log.debug("XendDomainInfo.device_create: %s" % dev_config)
+ dev_type = sxp.name(dev_config)
+ devid = self._createDevice(dev_type, dev_config)
+ self.info.device_add(dev_type, cfg_sxp = dev_config)
+ self._waitForDevice(dev_type, devid)
+ return self.getDeviceController(dev_type).sxpr(devid)
- return 1
+ def device_configure(self, dev_config, devid):
+ """Configure an existing device.
+
+ @param dev_config: device configuration
+ @type dev_config: dictionary (parsed config)
+ @param devid: device id
+ @type devid: int
+ """
+ deviceClass = sxp.name(dev_config)
+ self._reconfigureDevice(deviceClass, devid, dev_config)
+ def waitForDevices(self):
+ """Wait for this domain's configured devices to connect.
- def augmentInfo(self, priv):
- """Augment self.info, as given to us through {@link #recreate}, with
- values taken from the store. This recovers those values known to xend
- but not to the hypervisor.
+ @raise VmError: if any device fails to initialise.
"""
- def useIfNeeded(name, val):
- if not self.infoIsSet(name) and val is not None:
- self.info[name] = val
+ for devclass in XendDevices.valid_devices():
+ self.getDeviceController(devclass).waitForDevices()
- if priv:
- entries = VM_STORE_ENTRIES[:]
- entries.remove(('memory', int))
- entries.remove(('maxmem', int))
- else:
- entries = VM_STORE_ENTRIES
- entries.append(('image', str))
- entries.append(('security', str))
+ def destroyDevice(self, deviceClass, devid):
+ try:
+ devid = int(devid)
+ except ValueError:
+ # devid is not a number, let's search for it in xenstore.
+ devicePath = '%s/device/%s' % (self.dompath, deviceClass)
+ for entry in xstransact.List(devicePath):
+ backend = xstransact.Read('%s/%s' % (devicePath, entry),
+ "backend")
+ devName = xstransact.Read(backend, "dev")
+ if devName == devid:
+ # We found the integer matching our devid, use it instead
+ devid = entry
+ break
+
+ return self.getDeviceController(deviceClass).destroyDevice(devid)
- map(lambda x, y: useIfNeeded(x[0], y), entries,
- self.readVMDetails(entries))
- device = []
- for c in controllerClasses:
- devconfig = self.getDeviceConfigurations(c)
- if devconfig:
- device.extend(map(lambda x: (c, x), devconfig))
- useIfNeeded('device', device)
+ def getDeviceSxprs(self, deviceClass):
+ return self.getDeviceController(deviceClass).sxprs()
- def validateInfo(self):
- """Validate and normalise the info block. This has either been parsed
- by parseConfig, or received from xc through recreate and augmented by
- the current store contents.
+ def setMemoryTarget(self, target):
+ """Set the memory target of this domain.
+ @param target: In MiB.
"""
- def defaultInfo(name, val):
- if not self.infoIsSet(name):
- self.info[name] = val()
+ log.debug("Setting memory target of domain %s (%d) to %d MiB.",
+ self.info['name'], self.domid, target)
+
+ if target <= 0:
+ raise XendError('Invalid memory size')
+
+ self.info['memory'] = target
+ self.storeVm("memory", target)
+ self._storeDom("memory/target", target << 10)
+ def getVCPUInfo(self):
try:
- defaultInfo('name', lambda: "Domain-%d" % self.domid)
- defaultInfo('on_poweroff', lambda: "destroy")
- defaultInfo('on_reboot', lambda: "restart")
- defaultInfo('on_crash', lambda: "restart")
- defaultInfo('features', lambda: "")
- defaultInfo('cpu', lambda: None)
- defaultInfo('cpus', lambda: [])
- defaultInfo('cpu_cap', lambda: 0)
- defaultInfo('cpu_weight', lambda: 256)
-
- # some domains don't have a config file (e.g. dom0 )
- # to set number of vcpus so we derive available cpus
- # from max_vcpu_id which is present for running domains.
- if not self.infoIsSet('vcpus') and self.infoIsSet('max_vcpu_id'):
- avail = int(self.info['max_vcpu_id'])+1
- else:
- avail = int(1)
+ # We include the domain name and ID, to help xm.
+ sxpr = ['domain',
+ ['domid', self.domid],
+ ['name', self.info['name']],
+ ['vcpu_count', self.info['online_vcpus']]]
- defaultInfo('vcpus', lambda: avail)
- defaultInfo('online_vcpus', lambda: self.info['vcpus'])
- defaultInfo('max_vcpu_id', lambda: self.info['vcpus']-1)
- defaultInfo('vcpu_avail', lambda: (1 << self.info['vcpus']) - 1)
+ for i in range(0, self.info['max_vcpu_id']+1):
+ info = xc.vcpu_getinfo(self.domid, i)
- defaultInfo('memory', lambda: 0)
- defaultInfo('shadow_memory', lambda: 0)
- defaultInfo('maxmem', lambda: 0)
- defaultInfo('bootloader', lambda: None)
- defaultInfo('bootloader_args', lambda: None)
- defaultInfo('backend', lambda: [])
- defaultInfo('device', lambda: [])
- defaultInfo('image', lambda: None)
- defaultInfo('security', lambda: None)
+ 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']]])
- self.check_name(self.info['name'])
+ return sxpr
- if isinstance(self.info['image'], str):
- self.info['image'] = sxp.from_string(self.info['image'])
+ except RuntimeError, exn:
+ raise XendError(str(exn))
- if isinstance(self.info['security'], str):
- self.info['security'] = sxp.from_string(self.info['security'])
+ #
+ # internal functions ... TODO: re-categorised
+ #
- if self.info['memory'] == 0:
- if self.infoIsSet('mem_kb'):
- self.info['memory'] = (self.info['mem_kb'] + 1023) / 1024
- if self.info['memory'] <= 0:
- raise VmError('Invalid memory size')
+ 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.
+ """
+ def useIfNeeded(name, val):
+ if not self._infoIsSet(name) and val is not None:
+ self.info[name] = val
- if self.info['maxmem'] < self.info['memory']:
- self.info['maxmem'] = self.info['memory']
+ if priv:
+ entries = VM_STORE_ENTRIES[:]
+ entries.remove(('memory', int))
+ entries.remove(('maxmem', int))
+ else:
+ entries = VM_STORE_ENTRIES
+ entries.append(('image', str))
+ entries.append(('security', str))
- for (n, c) in self.info['device']:
- if not n or not c or n not in controllerClasses:
- raise VmError('invalid device (%s, %s)' %
- (str(n), str(c)))
+ map(lambda x, y: useIfNeeded(x[0], y), entries,
+ self._readVMDetails(entries))
+
+ devices = []
- for event in ['on_poweroff', 'on_reboot', 'on_crash']:
- if self.info[event] not in restart_modes:
- raise VmError('invalid restart event: %s = %s' %
- (event, str(self.info[event])))
+ for devclass in XendDevices.valid_devices():
+ devconfig = self.getDeviceController(devclass).configurations()
+ if devconfig:
+ devices.extend(map(lambda conf: (devclass, conf), devconfig))
- except KeyError, exn:
- log.exception(exn)
- raise VmError('Unspecified domain detail: %s' % exn)
+ if not self.info['device'] and devices is not None:
+ for device in devices:
+ self.info.device_add(device[0], cfg_sxp = device)
+ #
+ # Function to update xenstore /vm/*
+ #
- def readVm(self, *args):
+ def _readVm(self, *args):
return xstransact.Read(self.vmpath, *args)
- def writeVm(self, *args):
+ def _writeVm(self, *args):
return xstransact.Write(self.vmpath, *args)
- def removeVm(self, *args):
+ def _removeVm(self, *args):
return xstransact.Remove(self.vmpath, *args)
- def gatherVm(self, *args):
+ def _gatherVm(self, *args):
return xstransact.Gather(self.vmpath, *args)
-
- ## public:
-
def storeVm(self, *args):
return xstransact.Store(self.vmpath, *args)
+ #
+ # Function to update xenstore /dom/*
+ #
- ## private:
-
- def readDom(self, *args):
+ def _readDom(self, *args):
return xstransact.Read(self.dompath, *args)
- def writeDom(self, *args):
+ def _writeDom(self, *args):
return xstransact.Write(self.dompath, *args)
-
- ## public:
-
- def removeDom(self, *args):
+ def _removeDom(self, *args):
return xstransact.Remove(self.dompath, *args)
- def recreateDom(self):
- complete(self.dompath, lambda t: self._recreateDom(t))
+ def _storeDom(self, *args):
+ return xstransact.Store(self.dompath, *args)
+
+ def _recreateDom(self):
+ complete(self.dompath, lambda t: self._recreateDomFunc(t))
- def _recreateDom(self, t):
+ def _recreateDomFunc(self, t):
t.remove()
t.mkdir()
t.set_permissions({ 'dom' : self.domid })
+ t.write('vm', self.vmpath)
-
- ## private:
-
- def storeDom(self, *args):
- return xstransact.Store(self.dompath, *args)
-
-
- ## public:
-
- def completeRestore(self, store_mfn, console_mfn):
-
- log.debug("XendDomainInfo.completeRestore")
-
- self.store_mfn = store_mfn
- self.console_mfn = console_mfn
-
- self.introduceDomain()
- self.storeDomDetails()
- self.registerWatches()
- self.refreshShutdown()
-
- log.debug("XendDomainInfo.completeRestore done")
-
-
- def storeVmDetails(self):
- to_store = {}
-
- for k in VM_STORE_ENTRIES:
- if self.infoIsSet(k[0]):
- to_store[k[0]] = str(self.info[k[0]])
-
- if self.infoIsSet('image'):
- to_store['image'] = sxp.to_string(self.info['image'])
-
- if self.infoIsSet('security'):
- security = self.info['security']
- to_store['security'] = sxp.to_string(security)
- for idx in range(0, len(security)):
- if security[idx][0] == 'access_control':
- to_store['security/access_control'] = sxp.to_string([ security[idx][1] , security[idx][2] ])
- for aidx in range(1, len(security[idx])):
- if security[idx][aidx][0] == 'label':
- to_store['security/access_control/label'] = security[idx][aidx][1]
- if security[idx][aidx][0] == 'policy':
- to_store['security/access_control/policy'] = security[idx][aidx][1]
- if security[idx][0] == 'ssidref':
- to_store['security/ssidref'] = str(security[idx][1])
-
- if not self.readVm('xend/restart_count'):
- to_store['xend/restart_count'] = str(0)
-
- log.debug("Storing VM details: %s", 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 })
-
-
- def storeDomDetails(self):
+ def _storeDomDetails(self):
to_store = {
'domid': str(self.domid),
'vm': self.vmpath,
@@ -746,16 +698,13 @@ class XendDomainInfo:
f('store/port', self.store_port)
f('store/ring-ref', self.store_mfn)
- to_store.update(self.vcpuDomDetails())
+ to_store.update(self._vcpuDomDetails())
log.debug("Storing domain details: %s", to_store)
- self.writeDom(to_store)
+ self._writeDom(to_store)
-
- ## private:
-
- def vcpuDomDetails(self):
+ def _vcpuDomDetails(self):
def availability(n):
if self.info['vcpu_avail'] & (1 << n):
return 'online'
@@ -767,25 +716,80 @@ class XendDomainInfo:
result["cpu/%d/availability" % v] = availability(v)
return result
+ #
+ # xenstore watches
+ #
- ## public:
-
- def registerWatches(self):
+ 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.vmWatch = xswatch(self.vmpath, self._storeChanged)
self.shutdownWatch = xswatch(self.dompath + '/control/shutdown',
- self.handleShutdownWatch)
+ self._handleShutdownWatch)
+
+ def _storeChanged(self, _):
+ log.trace("XendDomainInfo.storeChanged");
+
+ changed = False
+
+ def f(x, y):
+ if y is not None and self.info[x[0]] != y:
+ self.info[x[0]] = y
+ changed = True
+
+ map(f, VM_CONFIG_PARAMS, self._readVMDetails(VM_CONFIG_PARAMS))
+
+ im = self._readVm('image')
+ current_im = self.info['image']
+ if (im is not None and
+ (current_im is None or sxp.to_string(current_im) != im)):
+ self.info['image'] = sxp.from_string(im)
+ 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):
- self.check_name(name)
+ self._checkName(name)
self.info['name'] = name
self.storeVm("name", name)
@@ -795,12 +799,13 @@ class XendDomainInfo:
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
@@ -812,11 +817,10 @@ class XendDomainInfo:
def getVCpuCount(self):
return self.info['vcpus']
-
def setVCpuCount(self, vcpus):
self.info['vcpu_avail'] = (1 << vcpus) - 1
self.storeVm('vcpu_avail', self.info['vcpu_avail'])
- self.writeDom(self.vcpuDomDetails())
+ self._writeDom(self._vcpuDomDetails())
def getLabel(self):
return security.get_security_info(self.info, 'label')
@@ -834,21 +838,23 @@ class XendDomainInfo:
def getWeight(self):
return self.info['cpu_weight']
- def endRestore(self):
- self.setResume(False)
-
def setResume(self, state):
self.info['resume'] = state
def getRestartCount(self):
- return self.readVm('xend/restart_count')
+ 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:
@@ -862,6 +868,7 @@ class XendDomainInfo:
# VM may have migrated to a different domain on this
# machine.
self.cleanupDomain()
+ self._stateSet(DOM_STATE_HALTED)
return
if xeninfo['dying']:
@@ -873,10 +880,11 @@ class XendDomainInfo:
# 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'):
+ if self._readDom('xend/shutdown_completed'):
# We've seen this shutdown already, but we are preserving
# the domain for debugging. Leave it alone.
return
@@ -888,9 +896,11 @@ class XendDomainInfo:
self.dumpCore()
restart_reason = 'crash'
+ self._stateSet(DOM_STATE_HALTED)
elif xeninfo['shutdown']:
- if self.readDom('xend/shutdown_completed'):
+ 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
@@ -901,15 +911,15 @@ class XendDomainInfo:
log.info('Domain has shutdown: name=%s id=%d reason=%s.',
self.info['name'], self.domid, reason)
- self.clearRestart()
+ self._clearRestart()
if reason == 'suspend':
- self.state_set(STATE_DOM_SHUTDOWN)
+ 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()
+ self._unwatchVm()
elif reason in ['poweroff', 'reboot']:
restart_reason = reason
else:
@@ -923,7 +933,11 @@ class XendDomainInfo:
else:
# Domain is alive. If we are shutting it down, then check
# the timeout on that, and destroy it if necessary.
-
+ if xeninfo['paused']:
+ self._stateSet(DOM_STATE_PAUSED)
+ else:
+ self._stateSet(DOM_STATE_RUNNING)
+
if self.shutdownStartTime:
timeout = (SHUTDOWN_TIMEOUT - time.time() +
self.shutdownStartTime)
@@ -936,61 +950,133 @@ class XendDomainInfo:
self.refresh_shutdown_lock.release()
if restart_reason:
- self.maybeRestart(restart_reason)
+ self._maybeRestart(restart_reason)
- def handleShutdownWatch(self, _):
- log.debug('XendDomainInfo.handleShutdownWatch')
-
- reason = self.readDom('control/shutdown')
+ #
+ # Restart functions - handling whether we come back up on 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
+ def _clearRestart(self):
+ self._removeDom("xend/shutdown_start_time")
- log.trace(
- "Scheduling refreshShutdown on domain %d in %ds.",
- self.domid, timeout)
- threading.Timer(timeout, self.refreshShutdown).start()
- return True
+ def _maybeRestart(self, reason):
+ # Dispatch to the correct method based upon the configured on_{reason}
+ # behaviour.
+ {"destroy" : self.destroy,
+ "restart" : self._restart,
+ "preserve" : self._preserve,
+ "rename-restart" : self._renameRestart}[self.info['on_' + reason]]()
- def shutdown(self, reason):
- if not reason in shutdown_reasons.values():
- raise XendError('Invalid reason: %s' % reason)
- if self.domid == 0:
- raise XendError("Can't specify Domain-0")
- self.storeDom("control/shutdown", reason)
+ def _renameRestart(self):
+ self._restart(True)
+ def _restart(self, rename = False):
+ """Restart the domain after it has exited.
- ## private:
+ @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
+
+ self._configureBootloader()
+ config = self.sxpr()
- def clearRestart(self):
- self.removeDom("xend/shutdown_start_time")
+ if self._infoIsSet('cpus') and len(self.info['cpus']) != 0:
+ config.append(['cpus', reduce(lambda x, y: str(x) + "," + str(y),
+ self.info['cpus'])])
+
+ 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')
- def maybeRestart(self, reason):
- # Dispatch to the correct method based upon the configured on_{reason}
- # behaviour.
- {"destroy" : self.destroy,
- "restart" : self.restart,
- "preserve" : self.preserve,
- "rename-restart" : self.renameRestart}[self.info['on_' + reason]]()
+ 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'], timeout)
+ self.destroy()
+ return
+ self._writeVm('xend/previous_restart_time', str(now))
- def renameRestart(self):
- self.restart(True)
+ try:
+ if rename:
+ self._preserveForRestart()
+ else:
+ self._unwatchVm()
+ self.destroyDomain()
+ # 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.
- def dumpCore(self,corefile=None):
+ new_dom = None
+ try:
+ new_dom = XendDomain.instance().domain_create(config)
+ new_dom.unpause()
+ rst_cnt = self._readVm('xend/restart_count')
+ rst_cnt = int(rst_cnt) + 1
+ self._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'], self.domid, self.info['uuid'],
+ new_name, new_uuid)
+ self._unwatchVm()
+ self._releaseDevices()
+ self.info['name'] = new_name
+ self.info['uuid'] = new_uuid
+ self.vmpath = XS_VMROOT + new_uuid
+ self._storeVmDetails()
+ self._preserve()
+
+
+ def _preserve(self):
+ log.info("Preserving dead domain %s (%d).", self.info['name'],
+ 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. Nothrow guarantee."""
try:
@@ -1011,260 +1097,132 @@ class XendDomainInfo:
self.domid, self.info['name'])
raise XendError("Failed to dump core: %s" % str(ex))
- ## public:
-
- def setMemoryTarget(self, target):
- """Set the memory target of this domain.
- @param target In MiB.
- """
- if target <= 0:
- raise XendError('Invalid memory size')
-
- log.debug("Setting memory target of domain %s (%d) to %d MiB.",
- self.info['name'], self.domid, target)
-
- self.info['memory'] = target
- self.storeVm("memory", target)
- self.storeDom("memory/target", target << 10)
+ #
+ # Device creation/deletion functions
+ #
+ def _createDevice(self, deviceClass, devConfig):
+ return self.getDeviceController(deviceClass).createDevice(devConfig)
- def update(self, info = None):
- """Update with info from xc.domain_getinfo().
- """
-
- log.trace("XendDomainInfo.update(%s) on domain %d", info, self.domid)
- if not info:
- info = dom_get(self.domid)
- if not info:
- return
-
- #manually update ssidref / security fields
- if security.on() and info.has_key('ssidref'):
- if (info['ssidref'] != 0) and self.info.has_key('security'):
- security_field = self.info['security']
- if not security_field:
- #create new security element
- self.info.update({'security': [['ssidref', str(info['ssidref'])]]})
- #ssidref field not used any longer
- info.pop('ssidref')
-
- self.info.update(info)
- self.validateInfo()
- self.refreshShutdown(info)
-
- log.trace("XendDomainInfo.update done on domain %d: %s", self.domid,
- self.info)
-
-
- ## private:
-
- def state_set(self, state):
- self.state_updated.acquire()
- try:
- if self.state != state:
- self.state = state
- self.state_updated.notifyAll()
- finally:
- self.state_updated.release()
-
-
- ## public:
-
- def waitForShutdown(self):
- self.state_updated.acquire()
- try:
- while self.state == STATE_DOM_OK:
- self.state_updated.wait()
- finally:
- self.state_updated.release()
-
-
- def __str__(self):
- s = "<domain"
- s += " id=" + str(self.domid)
- s += " name=" + self.info['name']
- s += " memory=" + str(self.info['memory'])
- s += ">"
- return s
-
- __repr__ = __str__
-
-
- ## private:
-
- def createDevice(self, deviceClass, devconfig):
- return self.getDeviceController(deviceClass).createDevice(devconfig)
-
-
- def waitForDevices_(self, deviceClass):
- return self.getDeviceController(deviceClass).waitForDevices()
-
-
- def waitForDevice(self, deviceClass, devid):
+ def _waitForDevice(self, deviceClass, devid):
return self.getDeviceController(deviceClass).waitForDevice(devid)
-
- def reconfigureDevice(self, deviceClass, devid, devconfig):
+ def _reconfigureDevice(self, deviceClass, devid, devconfig):
return self.getDeviceController(deviceClass).reconfigureDevice(
devid, devconfig)
+ def _createDevices(self):
+ """Create the devices for a vm.
- ## public:
-
- def destroyDevice(self, deviceClass, devid):
- if type(devid) is str:
- devicePath = '%s/device/%s' % (self.dompath, deviceClass)
- for entry in xstransact.List(devicePath):
- backend = xstransact.Read('%s/%s' % (devicePath, entry),
- "backend")
- devName = xstransact.Read(backend, "dev")
- if devName == devid:
- # We found the integer matching our devid, use it instead
- devid = entry
- break
- return self.getDeviceController(deviceClass).destroyDevice(devid)
-
-
- def getDeviceSxprs(self, deviceClass):
- return self.getDeviceController(deviceClass).sxprs()
-
+ @raise: VmError for invalid devices
+ """
+ for (devclass, config) in self.info.all_devices_sxpr():
+ log.info("createDevice: %s : %s" % (devclass, config))
+ self._createDevice(devclass, config)
- ## private:
+ if self.image:
+ self.image.createDeviceModel()
- def getDeviceConfigurations(self, deviceClass):
- return self.getDeviceController(deviceClass).configurations()
+ def _releaseDevices(self):
+ """Release all domain's devices. Nothrow guarantee."""
+ while True:
+ t = xstransact("%s/device" % self.dompath)
+ for devclass in XendDevices.valid_devices():
+ for dev in t.list(devclass):
+ try:
+ t.remove(dev)
+ 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'], devclass, dev)
+ if t.commit():
+ break
def getDeviceController(self, name):
- if name not in controllerClasses:
- raise XendError("unknown device type: " + str(name))
-
- return controllerClasses[name](self)
-
-
- ## public:
-
- def sxpr(self):
- sxpr = ['domain',
- ['domid', self.domid]]
-
- for e in ROUNDTRIPPING_CONFIG_ENTRIES:
- if self.infoIsSet(e[0]):
- sxpr.append([e[0], self.info[e[0]]])
-
- if self.infoIsSet('image'):
- sxpr.append(['image', self.info['image']])
-
- if self.infoIsSet('security'):
- sxpr.append(['security', self.info['security']])
-
- for cls in controllerClasses:
- for config in self.getDeviceConfigurations(cls):
- sxpr.append(['device', config])
-
- def stateChar(name):
- if name in self.info:
- if self.info[name]:
- return name[0]
- else:
- return '-'
- else:
- return '?'
+ """Get the device controller for this domain, and if it
+ doesn't exist, create it.
- state = reduce(
- lambda x, y: x + y,
- map(stateChar,
- ['running', 'blocked', 'paused', 'shutdown', 'crashed',
- 'dying']))
-
- sxpr.append(['state', state])
- if self.infoIsSet('shutdown'):
- reason = shutdown_reason(self.info['shutdown_reason'])
- sxpr.append(['shutdown_reason', reason])
- if self.infoIsSet('cpu_time'):
- sxpr.append(['cpu_time', self.info['cpu_time']/1e9])
- sxpr.append(['online_vcpus', self.info['online_vcpus']])
-
- if self.infoIsSet('start_time'):
- up_time = time.time() - self.info['start_time']
- sxpr.append(['up_time', str(up_time) ])
- sxpr.append(['start_time', str(self.info['start_time']) ])
-
- if self.store_mfn:
- sxpr.append(['store_mfn', self.store_mfn])
- if self.console_mfn:
- sxpr.append(['console_mfn', self.console_mfn])
-
- return sxpr
+ @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)
+ if rc != 0:
+ raise XendError("Device of type '%s' refuses migration." % n)
- def getVCPUInfo(self):
+ def migrateDevices(self, network, dst, step, domName=''):
+ """Notify the devices about migration
+ """
+ ctr = 0
try:
- # We include the domain name and ID, to help xm.
- sxpr = ['domain',
- ['domid', self.domid],
- ['name', self.info['name']],
- ['vcpu_count', self.info['online_vcpus']]]
-
- for i in range(0, self.info['max_vcpu_id']+1):
- info = xc.vcpu_getinfo(self.domid, i)
+ 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
- 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']]])
+ def migrateDevice(self, deviceClass, deviceConfig, network, dst,
+ step, domName=''):
+ return self.getDeviceController(deviceClass).migrate(deviceConfig,
+ network, dst, step, domName)
- return sxpr
+ def _recoverMigrateDevice(self, deviceClass, deviceConfig, network,
+ dst, step, domName=''):
+ return self.getDeviceController(deviceClass).recover_migrate(
+ deviceConfig, network, dst, step, domName)
- except RuntimeError, exn:
- raise XendError(str(exn))
-
## private:
- def check_name(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
- """
- if name is None or name == '':
- raise VmError('missing vm name')
- for c in name:
- if c in string.digits: continue
- if c in '_-.:/+': continue
- if c in string.ascii_letters: continue
- raise VmError('invalid vm name')
-
- dominfo = domain_by_name(name)
- if not dominfo:
- return
- if self.domid is None:
- raise VmError("VM name '%s' already in use by domain %d" %
- (name, dominfo.domid))
- if dominfo.domid != self.domid:
- raise VmError("VM name '%s' is used in both domains %d and %d" %
- (name, self.domid, dominfo.domid))
-
-
- def construct(self):
+ def _constructDomain(self):
"""Construct the domain.
@raise: VmError on error
"""
- log.debug('XendDomainInfo.construct: %s',
- self.domid)
+ log.debug('XendDomainInfo.constructDomain')
+
+ hvm = (self._infoIsSet('image') and
+ sxp.name(self.info['image']) == "hvm")
+ if hvm:
+ info = xc.xeninfo()
+ if not 'hvm' in info['xen_caps']:
+ raise VmError("HVM guest support is unavailable: is VT/AMD-V "
+ "supported by your CPU and enabled in your "
+ "BIOS?")
self.domid = xc.domain_create(
- dom = 0, ssidref = security.get_security_info(self.info, 'ssidref'),
- handle = uuid.fromString(self.info['uuid']))
+ domid = 0,
+ ssidref = security.get_security_info(self.info, 'ssidref'),
+ handle = uuid.fromString(self.info['uuid']),
+ hvm = int(hvm))
if self.domid < 0:
raise VmError('Creating domain failed: name=%s' %
@@ -1272,13 +1230,13 @@ class XendDomainInfo:
self.dompath = GetDomainPath(self.domid)
- self.recreateDom()
+ self._recreateDom()
# Set maximum number of vcpus in domain
xc.domain_max_vcpus(self.domid, int(self.info['vcpus']))
- def introduceDomain(self):
+ def _introduceDomain(self):
assert self.domid is not None
assert self.store_mfn is not None
assert self.store_port is not None
@@ -1289,25 +1247,25 @@ class XendDomainInfo:
raise XendError(str(exn))
- def initDomain(self):
+ def _initDomain(self):
log.debug('XendDomainInfo.initDomain: %s %s',
self.domid,
self.info['cpu_weight'])
# if we have a boot loader but no image, then we need to set things
# up by running the boot loader non-interactively
- if self.infoIsSet('bootloader') and not self.infoIsSet('image'):
- self.configure_bootloader()
+ if self._infoIsSet('bootloader') and not self._infoIsSet('image'):
+ self._configureBootloader()
- if not self.infoIsSet('image'):
+ if not self._infoIsSet('image'):
raise VmError('Missing image in configuration')
try:
self.image = image.create(self,
self.info['image'],
- self.info['device'])
+ self.info.all_devices_sxpr())
- localtime = self.info['localtime']
+ localtime = self.info.get('localtime', 0)
if localtime is not None and localtime == 1:
xc.domain_set_time_offset(self.domid)
@@ -1322,10 +1280,7 @@ class XendDomainInfo:
# Use architecture- and image-specific calculations to determine
# the various headrooms necessary, given the raw configured
- # values.
- # reservation, maxmem, memory, and shadow are all in KiB.
- reservation = self.image.getRequiredInitialReservation(
- self.info['memory'] * 1024)
+ # values. maxmem, memory, and shadow are all in KiB.
maxmem = self.image.getRequiredAvailableMemory(
self.info['maxmem'] * 1024)
memory = self.image.getRequiredAvailableMemory(
@@ -1348,11 +1303,7 @@ class XendDomainInfo:
shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024)
self.info['shadow_memory'] = shadow_cur
- # initial memory reservation
- xc.domain_memory_increase_reservation(self.domid, reservation, 0,
- 0)
-
- self.createChannels()
+ self._createChannels()
channel_details = self.image.createImage()
@@ -1360,21 +1311,21 @@ class XendDomainInfo:
if 'console_mfn' in channel_details:
self.console_mfn = channel_details['console_mfn']
- self.introduceDomain()
+ self._introduceDomain()
- self.createDevices()
+ self._createDevices()
if self.info['bootloader']:
self.image.cleanupBootloading()
self.info['start_time'] = time.time()
+ self._stateSet(DOM_STATE_RUNNING)
except RuntimeError, exn:
+ log.exception("XendDomainInfo.initDomain: exception occurred")
raise VmError(str(exn))
- ## public:
-
def cleanupDomain(self):
"""Cleanup domain resources; release devices. Idempotent. Nothrow
guarantee."""
@@ -1383,7 +1334,7 @@ class XendDomainInfo:
try:
self.unwatchShutdown()
- self.release_devices()
+ self._releaseDevices()
if self.image:
try:
@@ -1394,48 +1345,15 @@ class XendDomainInfo:
self.image = None
try:
- self.removeDom()
+ self._removeDom()
except:
log.exception("Removing domain path failed.")
- try:
- if not self.info['name'].startswith(ZOMBIE_PREFIX):
- self.info['name'] = ZOMBIE_PREFIX + self.info['name']
- except:
- log.exception("Renaming Zombie failed.")
-
- self.state_set(STATE_DOM_SHUTDOWN)
+ self._stateSet(DOM_STATE_HALTED)
finally:
self.refresh_shutdown_lock.release()
- def cleanupVm(self):
- """Cleanup VM resources. Idempotent. Nothrow guarantee."""
-
- self.unwatchVm()
-
- try:
- self.removeVm()
- except:
- log.exception("Removing VM path failed.")
-
-
- ## private:
-
- 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 unwatchShutdown(self):
"""Remove the watch on the domain's control/shutdown node, if any.
Idempotent. Nothrow guarantee. Expects to be protected by the
@@ -1450,84 +1368,142 @@ class XendDomainInfo:
except:
log.exception("Unwatching control/shutdown failed.")
+ def waitForShutdown(self):
+ self.state_updated.acquire()
+ try:
+ while self.state in (DOM_STATE_RUNNING,DOM_STATE_PAUSED):
+ self.state_updated.wait()
+ 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
- ## public:
+ self._introduceDomain()
+ self._storeDomDetails()
+ self._registerWatches()
+ self.refreshShutdown()
+
+ log.debug("XendDomainInfo.completeRestore done")
+
+
+ def _endRestore(self):
+ self.setResume(False)
+
+ #
+ # VM Destroy
+ #
def destroy(self):
"""Cleanup VM and destroy domain. Nothrow guarantee."""
- log.debug("XendDomainInfo.destroy: domid=%s", self.domid)
+ log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid))
- self.cleanupVm()
+ self._cleanupVm()
if self.dompath is not None:
- self.destroyDomain()
+ self.destroyDomain()
def destroyDomain(self):
- log.debug("XendDomainInfo.destroyDomain(%s)", self.domid)
+ log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid))
try:
if self.domid is not None:
xc.domain_destroy(self.domid)
+ self.domid = None
+ for state in DOM_STATES_OLD:
+ self.info[state] = 0
except:
log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
self.cleanupDomain()
- ## private:
-
- def release_devices(self):
- """Release all domain's devices. Nothrow guarantee."""
+ #
+ # Channels for xenstore and console
+ #
- while True:
- t = xstransact("%s/device" % self.dompath)
- for n in controllerClasses.keys():
- for d in t.list(n):
- try:
- t.remove(d)
- 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'], n, d)
- if t.commit():
- break
-
-
- def createChannels(self):
+ def _createChannels(self):
"""Create the channels to the domain.
"""
- self.store_port = self.createChannel()
- self.console_port = self.createChannel()
+ self.store_port = self._createChannel()
+ self.console_port = self._createChannel()
- def createChannel(self):
+ def _createChannel(self):
"""Create an event channel to the domain.
"""
try:
- return xc.evtchn_alloc_unbound(dom=self.domid, remote_dom=0)
+ return xc.evtchn_alloc_unbound(domid=self.domid, remote_dom=0)
except:
log.exception("Exception in alloc_unbound(%d)", self.domid)
raise
+ #
+ # Bootloader configuration
+ #
- ## public:
+ def _configureBootloader(self):
+ """Run the bootloader if we're configured to do so."""
+ if not self.info['bootloader']:
+ return
+ blcfg = None
+ # FIXME: this assumes that we want to use the first disk device
+ for (n, c) in self.info.all_devices_sxpr():
+ if not n or not c or not(n in ["vbd", "tap"]):
+ continue
+ disk = sxp.child_value(c, "uname")
+ if disk is None:
+ continue
+ fn = blkdev_uname_to_file(disk)
+ blcfg = bootloader(self.info['bootloader'], fn, 1,
+ self.info['bootloader_args'],
+ self.info['image'])
+ break
+ if blcfg is None:
+ msg = "Had a bootloader specified, but can't find disk"
+ log.error(msg)
+ raise VmError(msg)
+ self.info['image'] = blcfg
- def createDevices(self):
- """Create the devices for a vm.
+ #
+ # VM Functions
+ #
- @raise: VmError for invalid devices
+ 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 []
- for (n, c) in self.info['device']:
- self.createDevice(n, c)
+ def _cleanupVm(self):
+ """Cleanup VM resources. Idempotent. Nothrow guarantee."""
- if self.image:
- self.image.createDeviceModel()
+ self._unwatchVm()
+
+ try:
+ self._removeVm()
+ except:
+ log.exception("Removing VM path failed.")
- ## public:
def checkLiveMigrateMemory(self):
""" Make sure there's enough memory to migrate this domain """
@@ -1542,14 +1518,9 @@ class XendDomainInfo:
if overhead_kb > 0:
balloon.free(overhead_kb)
- 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['device']:
- rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST)
- if rc != 0:
- raise XendError("Device of type '%s' refuses migration." % n)
+ def _unwatchVm(self):
+ """Remove the watch on the VM path, if any. Idempotent. Nothrow
+ guarantee."""
def testDeviceComplete(self):
""" For Block IO migration safety we must ensure that
@@ -1571,238 +1542,427 @@ class XendDomainInfo:
log.info("Dev still active but hit max loop timeout")
break
- def migrateDevices(self, network, dst, step, domName=''):
- """Notify the devices about migration
- """
- ctr = 0
- try:
- for (n, c) in self.info['device']:
- self.migrateDevice(n, c, network, dst, step, domName)
- ctr = ctr + 1
- except:
- for (n, c) in self.info['device']:
- if ctr == 0:
- step = step - 1
- ctr = ctr - 1
- self.recoverMigrateDevice(n, c, network, dst, step, domName)
- raise
+ def _storeVmDetails(self):
+ to_store = {}
- def migrateDevice(self, deviceClass, deviceConfig, network, dst,
- step, domName=''):
- return self.getDeviceController(deviceClass).migrate(deviceConfig,
- network, dst, step, domName)
+ for k in VM_STORE_ENTRIES:
+ if self._infoIsSet(k[0]):
+ to_store[k[0]] = str(self.info[k[0]])
- def recoverMigrateDevice(self, deviceClass, deviceConfig, network,
- dst, step, domName=''):
- return self.getDeviceController(deviceClass).recover_migrate(
- deviceConfig, network, dst, step, domName)
+ if self._infoIsSet('image'):
+ to_store['image'] = sxp.to_string(self.info['image'])
- def waitForDevices(self):
- """Wait for this domain's configured devices to connect.
+ if self._infoIsSet('security'):
+ secinfo = self.info['security']
+ to_store['security'] = sxp.to_string(secinfo)
+ for idx in range(0, len(secinfo)):
+ if secinfo[idx][0] == 'access_control':
+ to_store['security/access_control'] = sxp.to_string(
+ [secinfo[idx][1], secinfo[idx][2]])
+ for aidx in range(1, len(secinfo[idx])):
+ if secinfo[idx][aidx][0] == 'label':
+ to_store['security/access_control/label'] = \
+ secinfo[idx][aidx][1]
+ if secinfo[idx][aidx][0] == 'policy':
+ to_store['security/access_control/policy'] = \
+ secinfo[idx][aidx][1]
+ if secinfo[idx][0] == 'ssidref':
+ to_store['security/ssidref'] = str(secinfo[idx][1])
+
+
+ if not self._readVm('xend/restart_count'):
+ to_store['xend/restart_count'] = str(0)
- @raise: VmError if any device fails to initialise.
- """
- for c in controllerClasses:
- self.waitForDevices_(c)
+ log.debug("Storing VM details: %s", to_store)
+ self._writeVm(to_store)
+ self._setVmPermissions()
- def device_create(self, dev_config):
- """Create a new device.
- @param dev_config: device configuration
- """
- dev_type = sxp.name(dev_config)
- devid = self.createDevice(dev_type, dev_config)
- self.waitForDevice(dev_type, devid)
- self.info['device'].append((dev_type, dev_config))
- return self.getDeviceController(dev_type).sxpr(devid)
+ 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 device_configure(self, dev_config):
- """Configure an existing device.
- @param dev_config: device configuration
- """
- deviceClass = sxp.name(dev_config)
- self.reconfigureDevice(deviceClass, None, dev_config)
+ def _stateSet(self, state):
+ self.state_updated.acquire()
+ try:
+ if self.state != state:
+ self.state = state
+ self.state_updated.notifyAll()
+ finally:
+ self.state_updated.release()
+ def _infoIsSet(self, name):
+ return name in self.info and self.info[name] is not None
- def pause(self):
- xc.domain_pause(self.domid)
+ 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')
- def unpause(self):
- xc.domain_unpause(self.domid)
+ 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 != self and not dom.info['dying']:
+ raise VmError("VM name '%s' already exists" % name)
+
- ## private:
+ def update(self, info = None, refresh = True):
+ """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
+
+ #manually update ssidref / security fields
+ if security.on() and info.has_key('ssidref'):
+ if (info['ssidref'] != 0) and self.info.has_key('security'):
+ security_field = self.info['security']
+ if not security_field:
+ #create new security element
+ self.info.update({'security':
+ [['ssidref', str(info['ssidref'])]]})
+ #ssidref field not used any longer
+ if 'ssidref' in info:
+ info.pop('ssidref')
- def restart(self, rename = False):
- """Restart the domain after it has exited.
+ # make sure state is reset for info
+ # TODO: we should eventually get rid of old_dom_states
- @param rename True if the old domain is to be renamed and preserved,
- False if it is to be destroyed.
+ self.info.update(info)
+ self.info.validate()
+
+ if refresh:
+ self.refreshShutdown(info)
+
+ log.trace("XendDomainInfo.update done on domain %s: %s",
+ str(self.domid), self.info)
+
+ def sxpr(self, ignore_devices = False):
+ return self.info.get_sxp(domain = self,
+ ignore_devices = ignore_devices)
+
+ # 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['maxmem']
+ def get_memory_static_min(self):
+ return self.info['memory']
+ def get_vcpus_policy(self):
+ sched_id = 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_vcpus_params(self):
+ return '' # TODO
+ def get_power_state(self):
+ return XEN_API_VM_POWER_STATE[self.state]
+ def get_bios_boot(self):
+ return '' # TODO
+ def get_platform_std_vga(self):
+ return False
+ def get_platform_serial(self):
+ return '' # TODO
+ def get_platform_localtime(self):
+ return False # TODO
+ def get_platform_clock_offset(self):
+ return False # TODO
+ def get_platform_enable_audio(self):
+ return False # TODO
+ def get_builder(self):
+ return 'Linux' # TODO
+ def get_boot_method(self):
+ bootloader = self.info['bootloader']
+ if not bootloader or bootloader not in XEN_API_BOOT_TYPE:
+ return 'kernel_external'
+ return bootloader
+
+ def get_kernel_image(self):
+ return self.info['kernel_kernel']
+ def get_kernel_initrd(self):
+ return self.info['kernel_initrd']
+ def get_kernel_args(self):
+ return self.info['kernel_args']
+ def get_grub_cmdline(self):
+ return '' # TODO
+ def get_pci_bus(self):
+ return 0 # TODO
+ def get_tools_version(self):
+ return {} # TODO
+ def get_other_config(self):
+ return {} # TODO
+
+ def get_on_shutdown(self):
+ after_shutdown = self.info.get('on_poweroff')
+ 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('on_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):
+ after_suspend = self.info.get('on_suspend') # TODO: not supported
+ 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('on_crash')
+ if not after_crash or after_crash not in XEN_API_ON_CRASH_BEHAVIOUR:
+ return XEN_API_ON_CRASH_BEHAVIOUR[0]
+ return 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_config = self.info['device'].get(dev_uuid)
+
+ # shortcut if the domain isn't started because
+ # the devcontrollers will have no better information
+ # than XendConfig.
+ if self.state in (XEN_API_VM_POWER_STATE_HALTED,):
+ if dev_type_config:
+ return copy.deepcopy(dev_type_config[1])
+ return None
+
+ # instead of using dev_class, we use the dev_type
+ # that is from XendConfig.
+ # This will accomdate 'tap' as well as 'vbd'
+ dev_type = dev_type_config[0]
+
+ controller = self.getDeviceController(dev_type)
+ if not controller:
+ return None
+
+ all_configs = controller.getAllDeviceConfigurations()
+ if not all_configs:
+ return None
+
+ dev_config = copy.deepcopy(dev_type_config[1])
+ for _devid, _devcfg in all_configs.items():
+ if _devcfg.get('uuid') == dev_uuid:
+ dev_config.update(_devcfg)
+ dev_config['id'] = _devid
+ return dev_config
+
+ return 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%d' % devid
+ else:
+ config['device'] = ''
+
+ config['network'] = '' # Invalid for Xend
+ config['MTU'] = 1500 # TODO
+ config['network_read_kbs'] = 0.0
+ config['network_write_kbs'] = 0.0
+ config['IO_bandwidth_incoming_kbs'] = 0.0
+ config['IO_bandwidth_outgoing_kbs'] = 0.0
+
+ if dev_class =='vbd':
+ config['VDI'] = '' # TODO
+ config['device'] = config.get('dev', '')
+ config['driver'] = 'paravirtualised' # TODO
+ config['image'] = config.get('uname', '')
+ config['IO_bandwidth_incoming_kbs'] = 0.0
+ config['IO_bandwidth_outgoing_kbs'] = 0.0
+ if config['mode'] == 'r':
+ config['mode'] = 'RO'
+ else:
+ config['mode'] = 'RW'
- self.configure_bootloader()
- config = self.sxpr()
+ if dev_class == 'vtpm':
+ config['driver'] = 'paravirtualised' # TODO
- if self.infoIsSet('cpus') and len(self.info['cpus']) != 0:
- config.append(['cpus', reduce(lambda x, y: str(x) + "," + str(y),
- self.info['cpus'])])
+ return config
- if self.readVm(RESTART_IN_PROGRESS):
- log.error('Xend failed during restart of domain %d. '
- 'Refusing to restart to avoid loops.',
- self.domid)
- self.destroy()
- return
+ 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 get_vcpus_util(self):
+ # TODO: this returns the total accum cpu time, rather than util
+ # TODO: spec says that key is int, however, python does not allow
+ # non-string keys to dictionaries.
+ vcpu_util = {}
+ if 'max_vcpu_id' in self.info and self.domid != None:
+ for i in range(0, self.info['max_vcpu_id']+1):
+ info = xc.vcpu_getinfo(self.domid, i)
+ vcpu_util[str(i)] = info['cpu_time']/1000000000.0
+
+ return vcpu_util
- self.writeVm(RESTART_IN_PROGRESS, 'True')
+ def get_vifs(self):
+ return self.info.get('vif_refs', [])
- 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'], timeout)
- self.destroy()
- return
+ def get_vbds(self):
+ return self.info.get('vbd_refs', [])
- self.writeVm('xend/previous_restart_time', str(now))
+ def get_vtpms(self):
+ return self.info.get('vtpm_refs', [])
- try:
- if rename:
- self.preserveForRestart()
- else:
- self.unwatchVm()
- self.destroyDomain()
+ def create_vbd(self, xenapi_vbd):
+ """Create a VBD device from the passed struct in Xen API format.
- # 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.
+ @return: uuid of the device
+ @rtype: string
+ """
- new_dom = None
- try:
- new_dom = XendDomain.instance().domain_create(config)
- new_dom.unpause()
- rst_cnt = self.readVm('xend/restart_count')
- rst_cnt = int(rst_cnt) + 1
- self.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 %d.', self.domid)
+ dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd)
+ if not dev_uuid:
+ raise XendError('Failed to create device')
+
+ if self.state in (XEN_API_VM_POWER_STATE_RUNNING,):
+ sxpr = self.info.device_sxpr(dev_uuid)
+ devid = self.getDeviceController('vbd').createDevice(sxpr)
+ raise XendError("Device creation failed")
+ return dev_uuid
- 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).
+ def create_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
"""
-
- new_name = self.generateUniqueName()
- new_uuid = uuid.toString(uuid.create())
- log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
- self.info['name'], self.domid, self.info['uuid'],
- new_name, new_uuid)
- self.unwatchVm()
- self.release_devices()
- self.info['name'] = new_name
- self.info['uuid'] = new_uuid
- self.vmpath = XendDomain.VMROOT + new_uuid
- self.storeVmDetails()
- self.preserve()
+ xenapi_vbd['image'] = vdi_image_path
+ log.debug('create_vbd_with_vdi: %s' % xenapi_vbd)
+ dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd)
+ if not dev_uuid:
+ raise XendError('Failed to create device')
+ if self.state in (XEN_API_VM_POWER_STATE_RUNNING,):
+ sxpr = self.info.device_sxpr(dev_uuid)
+ devid = self.getDeviceController('tap').createDevice(sxpr)
+ raise XendError("Device creation failed")
- def preserve(self):
- log.info("Preserving dead domain %s (%d).", self.info['name'],
- self.domid)
- self.unwatchVm()
- self.storeDom('xend/shutdown_completed', 'True')
- self.state_set(STATE_DOM_SHUTDOWN)
+ return dev_uuid
+ def create_vif(self, xenapi_vif):
+ """Create VIF device from the passed struct in Xen API format.
- # private:
+ @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.state in (DOM_STATE_HALTED,):
+ sxpr = self.info.device_sxpr(dev_uuid)
+ devid = self.getDeviceController('vif').createDevice(sxpr)
+ raise XendError("Device creation failed")
- def generateUniqueName(self):
- n = 1
- while True:
- name = "%s-%d" % (self.info['name'], n)
- try:
- self.check_name(name)
- return name
- except VmError:
- n += 1
+ return dev_uuid
+ def create_vtpm(self, xenapi_vtpm):
+ """Create a VTPM device from the passed struct in Xen API format.
- def configure_bootloader(self):
- """Run the bootloader if we're configured to do so."""
- if not self.info['bootloader']:
- return
- blcfg = None
- # FIXME: this assumes that we want to use the first disk device
- for (n,c) in self.info['device']:
- if not n or not c or not(n in ["vbd", "tap"]):
- continue
- disk = sxp.child_value(c, "uname")
- if disk is None:
- continue
- fn = blkdev_uname_to_file(disk)
- blcfg = bootloader(self.info['bootloader'], fn, 1,
- self.info['bootloader_args'],
- self.info['image'])
- break
- if blcfg is None:
- msg = "Had a bootloader specified, but can't find disk"
- log.error(msg)
- raise VmError(msg)
- self.info['image'] = blcfg
+ @return: uuid of the device
+ @rtype: string
+ """
+ if self.state 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')
- def send_sysrq(self, key):
- asserts.isCharConvertible(key)
+ return dev_uuid
- self.storeDom("control/sysrq", '%c' % key)
+ def has_device(self, dev_class, dev_uuid):
+ return (dev_uuid in self.info['%s_refs' % dev_class])
+ """
+ def stateChar(name):
+ if name in self.info:
+ if self.info[name]:
+ return name[0]
+ else:
+ return '-'
+ else:
+ return '?'
- def infoIsSet(self, name):
- return name in self.info and self.info[name] is not None
+ state = reduce(lambda x, y: x + y, map(stateChar, DOM_STATES_OLD))
+ sxpr.append(['state', state])
-#============================================================================
-# Register device controllers and their device config types.
+ if self.store_mfn:
+ sxpr.append(['store_mfn', self.store_mfn])
+ if self.console_mfn:
+ sxpr.append(['console_mfn', self.console_mfn])
+ """
+
+ def __str__(self):
+ return '<domain id=%s name=%s memory=%s state=%s>' % \
+ (str(self.domid), self.info['name'],
+ str(self.info['memory']), DOM_STATES[self.state])
-"""A map from device-class names to the subclass of DevController that
-implements the device control specific to that device-class."""
-controllerClasses = {}
+ __repr__ = __str__
-def addControllerClass(device_class, cls):
- """Register a subclass of DevController to handle the named device-class.
- """
- cls.deviceClass = device_class
- controllerClasses[device_class] = cls
-
-
-from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif
-from xen.xend.server.BlktapController import BlktapController
-addControllerClass('vbd', blkif.BlkifController)
-addControllerClass('vif', netif.NetifController)
-addControllerClass('vtpm', tpmif.TPMifController)
-addControllerClass('pci', pciif.PciController)
-addControllerClass('ioports', iopif.IOPortsController)
-addControllerClass('irq', irqif.IRQController)
-addControllerClass('usb', usbif.UsbifController)
-addControllerClass('tap', BlktapController)
diff --git a/tools/python/xen/xend/XendError.py b/tools/python/xen/xend/XendError.py
index f78a71f031..5947145267 100644
--- a/tools/python/xen/xend/XendError.py
+++ b/tools/python/xen/xend/XendError.py
@@ -34,6 +34,20 @@ class XendError(Fault):
class VmError(XendError):
"""Vm construction error."""
-
pass
+
+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_TODO = ('ETODO', 'Lazy Programmer Error')
diff --git a/tools/python/xen/xend/XendNode.py b/tools/python/xen/xend/XendNode.py
index 54cb30628a..b6eafa3331 100644
--- a/tools/python/xen/xend/XendNode.py
+++ b/tools/python/xen/xend/XendNode.py
@@ -13,22 +13,40 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#============================================================================
# Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
+# Copyright (c) 2006 Xensource Inc.
#============================================================================
-"""Handler for node operations.
- Has some persistent state:
- - logs
- - notification urls
-
-"""
-
import os
+import socket
import xen.lowlevel.xc
+from xen.xend import uuid
+from xen.xend.XendError import XendError
+from xen.xend.XendStorageRepository import XendStorageRepository
class XendNode:
-
+ """XendNode - Represents a Domain 0 Host."""
+
def __init__(self):
self.xc = xen.lowlevel.xc.xc()
+ self.uuid = uuid.createString()
+ self.cpus = {}
+ self.name = socket.gethostname()
+ self.desc = ""
+ self.sr = XendStorageRepository()
+
+ physinfo = self.physinfo_dict()
+ cpu_count = physinfo['nr_cpus']
+ cpu_features = physinfo['hw_caps']
+
+ for i in range(cpu_count):
+ # construct uuid by appending extra bit on the host.
+ # since CPUs belong to a host.
+ cpu_uuid = self.uuid + '-%04d' % i
+ cpu_info = {'uuid': cpu_uuid,
+ 'host': self.uuid,
+ 'number': i,
+ 'features': cpu_features}
+ self.cpus[cpu_uuid] = cpu_info
def shutdown(self):
return 0
@@ -39,6 +57,88 @@ class XendNode:
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)
+
+ #
+ # Storage Repo
+ #
+
+ def get_sr(self):
+ return self.sr
+
+ #
+ # Host Functions
+ #
+
+ def xen_version(self):
+ info = self.xc.xeninfo()
+ try:
+ from xen import VERSION
+ return {'Xen': '%(xen_major)d.%(xen_minor)d' % info,
+ 'Xend': VERSION}
+ except (ImportError, AttributeError):
+ return {'Xen': '%(xen_major)d.%(xen_minor)d' % info,
+ 'Xend': '3.0.3'}
+
+ 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
+
+ #
+ # 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_features(self, host_cpu_ref):
+ try:
+ return self.cpus[host_cpu_ref]['features']
+ except KeyError:
+ raise XendError('Invalid CPU Reference')
+
+ def get_host_cpu_number(self, host_cpu_ref):
+ try:
+ return self.cpus[host_cpu_ref]['number']
+ except KeyError:
+ raise XendError('Invalid CPU Reference')
+
+ def get_host_cpu_load(self, host_cpu_ref):
+ return 0.0
+
+
+
+ #
+ # Getting host information.
+ #
+
def info(self):
return (self.nodeinfo() + self.physinfo() + self.xeninfo() +
self.xendinfo())
@@ -98,6 +198,19 @@ class XendNode:
def xendinfo(self):
return [['xend_config_format', 2]]
+ # 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
diff --git a/tools/python/xen/xend/XendProtocol.py b/tools/python/xen/xend/XendProtocol.py
index cb1e0f7f99..f09f6762d3 100644
--- a/tools/python/xen/xend/XendProtocol.py
+++ b/tools/python/xen/xend/XendProtocol.py
@@ -22,7 +22,7 @@ import time
import types
from encode import *
-import sxp
+from xen.xend import sxp
from xen.xend import XendRoot
diff --git a/tools/python/xen/xend/XendRoot.py b/tools/python/xen/xend/XendRoot.py
index 1ff9baf485..e098c9fad8 100644
--- a/tools/python/xen/xend/XendRoot.py
+++ b/tools/python/xen/xend/XendRoot.py
@@ -30,12 +30,8 @@ import os.path
import string
import sys
-import osdep
-import XendLogging
-from XendError import XendError
-
-import sxp
-
+from xen.xend import sxp, osdep, XendLogging
+from xen.xend.XendError import XendError
class XendRoot:
"""Root of the management classes."""
@@ -102,6 +98,9 @@ class XendRoot:
"""Default interface to listen for VNC connections on"""
xend_vnc_listen_default = '127.0.0.1'
+ """Default session storage path."""
+ xend_domains_path_default = '/var/lib/xend/domains'
+
components = {}
def __init__(self):
@@ -196,15 +195,18 @@ class XendRoot:
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)
+ return self.get_config_bool("xend-tcp-xmlrpc-server",
+ self.xend_tcp_xmlrpc_server_default)
def get_xend_unix_xmlrpc_server(self):
- return self.get_config_bool("xend-unix-xmlrpc-server", self.xend_unix_xmlrpc_server_default)
+ 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)
+ return self.get_config_bool("xend-relocation-server",
+ self.xend_relocation_server_default)
def get_xend_port(self):
"""Get the port xend listens at for its HTTP interface.
@@ -214,7 +216,8 @@ class XendRoot:
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)
+ return self.get_config_int('xend-relocation-port',
+ self.xend_relocation_port_default)
def get_xend_relocation_hosts_allow(self):
return self.get_config_value("xend-relocation-hosts-allow",
@@ -246,6 +249,11 @@ class XendRoot:
"""
return self.get_config_value("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_value("xend-domains-path", self.xend_domains_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."""
diff --git a/tools/python/xen/xend/XendStorageRepository.py b/tools/python/xen/xend/XendStorageRepository.py
new file mode 100644
index 0000000000..c2fbdb1294
--- /dev/null
+++ b/tools/python/xen/xend/XendStorageRepository.py
@@ -0,0 +1,381 @@
+#!/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.
+#============================================================================
+#
+# The default QCOW Xen API Storage Repository
+#
+
+import os
+import commands
+import threading
+
+from xen.xend import uuid
+from xen.xend.XendError import XendError
+from xen.xend.XendVDI import *
+
+XEND_STORAGE_MAX_IGNORE = -1
+XEND_STORAGE_DIR = "/var/lib/xend/storage/"
+XEND_STORAGE_QCOW_FILENAME = "%s.qcow"
+XEND_STORAGE_VDICFG_FILENAME = "%s.vdi.xml"
+DF_COMMAND = "df -lPk"
+QCOW_CREATE_COMMAND = "/usr/sbin/qcow-create %d %s"
+
+KB = 1024
+MB = 1024 *1024
+
+class DeviceInvalidError(Exception):
+ pass
+
+class 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 <uuid>.img and <uuid>.qcow.
+ """
+
+ def __init__(self, storage_dir = XEND_STORAGE_DIR,
+ storage_max = XEND_STORAGE_MAX_IGNORE):
+ """
+ @keyword storage_dir: Where the images will be stored.
+ @type storage_dir: string
+ @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.
+ """
+
+ self.storage_dir = storage_dir
+ self.storage_max = storage_max
+ self.storage_free = 0
+ self.images = {}
+
+ # XenAPI Parameters
+ self.uuid = self._sr_uuid()
+ self.type = "qcow-file"
+ self.location = self.storage_dir
+ self.name_label = "Local"
+ self.name_description = "Xend Storage Repository"
+
+ self.lock = threading.RLock()
+ self._refresh()
+
+ def _sr_uuid(self):
+ uuid_file = os.path.join(XEND_STORAGE_DIR, 'uuid')
+ try:
+ if uuid_file and os.path.exists(uuid_file):
+ return open(uuid_file, 'r').read().strip()
+ else:
+ new_uuid = uuid.createString()
+ open(uuid_file, 'w').write(new_uuid + '\n')
+ return new_uuid
+ except IOError:
+ # TODO: log warning
+ pass
+
+ return uuid.createString()
+
+ def _refresh(self):
+ """Internal function that refreshes the state of the disk and
+ updates the list of images available.
+ """
+ self.lock.acquire()
+ try:
+ # create directory if /var/lib/xend/storage does not exist
+ if not os.path.exists(XEND_STORAGE_DIR):
+ os.makedirs(XEND_STORAGE_DIR)
+ os.chmod(XEND_STORAGE_DIR, 0700)
+
+ # scan the directory and populate self.images
+ total_used = 0
+ seen_images = []
+ for filename in os.listdir(XEND_STORAGE_DIR):
+ if filename[-5:] == XEND_STORAGE_QCOW_FILENAME[-5:]:
+ image_uuid = filename[:-5]
+ seen_images.append(image_uuid)
+
+ # add this image if we haven't seen it before
+ if image_uuid not in self.images:
+ qcow_file = XEND_STORAGE_QCOW_FILENAME % image_uuid
+ cfg_file = XEND_STORAGE_VDICFG_FILENAME % image_uuid
+ qcow_path = os.path.join(XEND_STORAGE_DIR, qcow_file)
+ cfg_path = os.path.join(XEND_STORAGE_DIR, cfg_file)
+
+ qcow_size = os.stat(qcow_path).st_size
+
+ # TODO: no way to stat virtual size of qcow
+ vdi = XendQCOWVDI(image_uuid, self.uuid,
+ qcow_path, cfg_path,
+ qcow_size, qcow_size)
+
+ if cfg_path and os.path.exists(cfg_path):
+ vdi.load_config(cfg_path)
+
+ self.images[image_uuid] = vdi
+ total_used += qcow_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]
+
+ # update free storage if we have to track that
+ if self.storage_max != XEND_STORAGE_MAX_IGNORE:
+ self.storage_free = self.storage_max - total_used
+ else:
+ self.storage_free = self._get_free_space()
+
+ finally:
+ self.lock.release()
+
+ def _get_df(self):
+ """Returns the output of 'df' in a dictionary where the keys
+ are the Linux device numbers, and the values are it's corresponding
+ free space in bytes
+
+ @rtype: dictionary
+ """
+ df = commands.getoutput(DF_COMMAND)
+ devnum_free = {}
+ for line in df.split('\n')[1:]:
+ words = line.split()
+ mount_point = words[-1]
+ dev_no = os.stat(mount_point).st_dev
+ free_kb = int(words[3])
+ devnum_free[dev_no] = free_kb * KB
+ return devnum_free
+
+ 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
+ """
+ df = self._get_df()
+ devnum = os.stat(self.storage_dir).st_dev
+ if df.has_key(devnum):
+ return df[devnum]
+ raise DeviceInvalidError("Device not found for storage path: %s" %
+ self.storage_dir)
+
+ 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.storage_max != -1:
+ return self.storage_free
+
+ bytes_free = self._get_free_space()
+ try:
+ if size_bytes < bytes_free:
+ return True
+ except DeviceInvalidError:
+ pass
+ return False
+
+ def _create_image_files(self, desired_size_bytes):
+ """Create an image and return its assigned UUID.
+
+ @param desired_size_kb: Desired image size in KB.
+ @type desired_size_kb: 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")
+
+ image_uuid = uuid.createString()
+ qcow_path = os.path.join(XEND_STORAGE_DIR,
+ 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_image(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:
+ # TODO: log warning
+ pass
+ del self.images[image_uuid]
+ 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.storage_max != XEND_STORAGE_MAX_IGNORE:
+ return self.storage_max
+ else:
+ return self.free_space_bytes() + self.used_space_bytes()
+ 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:
+ total_used = 0
+ for val in self.images.values():
+ total_used += val.physical_utilisation
+ return total_used
+ finally:
+ self.lock.release()
+
+ def is_valid_vdi(self, vdi_uuid):
+ return (vdi_uuid in self.images)
+
+ def create_image(self, vdi_struct):
+ image_uuid = None
+ try:
+ sector_count = int(vdi_struct.get('virtual_size', 0))
+ sector_size = int(vdi_struct.get('sector_size', 1024))
+ size_bytes = (sector_count * sector_size)
+
+ image_uuid = self._create_image_files(size_bytes)
+ image = self.images[image_uuid]
+ image_cfg = {
+ 'sector_size': sector_size,
+ 'virtual_size': sector_count,
+ '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(XEND_STORAGE_DIR, cfg_filename)
+ image.save_config(cfg_path)
+
+ except Exception, e:
+ # cleanup before raising exception
+ if image_uuid:
+ self.destroy_image(image_uuid)
+
+ raise
+
+ return image_uuid
+
+ def xen_api_get_by_label(self, label):
+ self.lock.acquire()
+ try:
+ for image_uuid, val in self.images.values():
+ if val.name_label == label:
+ return image_uuid
+ return None
+ finally:
+ self.lock.release()
+
+ def xen_api_get_by_uuid(self, image_uuid):
+ self.lock.acquire()
+ try:
+ return self.images.get(image_uuid)
+ finally:
+ self.lock.release()
+
+
+# remove everything below this line!!
+if __name__ == "__main__":
+ xsr = XendStorageRepository()
+ print 'Free Space: %d MB' % (xsr.free_space_bytes()/MB)
+ print "Create Image:",
+ print xsr._create_image_files(10 * MB)
+ print 'Delete all images:'
+ for image_uuid in xsr.list_images():
+ print image_uuid,
+ xsr._destroy_image_files(image_uuid)
+
+ print
diff --git a/tools/python/xen/xend/XendVDI.py b/tools/python/xen/xend/XendVDI.py
new file mode 100644
index 0000000000..22a1616360
--- /dev/null
+++ b/tools/python/xen/xend/XendVDI.py
@@ -0,0 +1,155 @@
+#!/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
+
+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',
+ 'sector_size',
+ 'virtual_size',
+ 'physical_utilisation',
+ 'parent',
+ 'children',
+ '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.sector_size = 1024
+ self.virtual_size = 0
+ self.physical_utilisation = 0
+ self.parent = None
+ self.children = []
+ self.sharable = False
+ self.read_only = False
+ self.type = "system"
+
+ 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
+
+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.sector_size = 512
+ self.auto_save = True
+
diff --git a/tools/python/xen/xend/image.py b/tools/python/xen/xend/image.py
index 8a761d89cf..8c1f21a52e 100644
--- a/tools/python/xen/xend/image.py
+++ b/tools/python/xen/xend/image.py
@@ -24,7 +24,7 @@ import signal
import xen.lowlevel.xc
from xen.xend import sxp
-from xen.xend.XendError import VmError
+from xen.xend.XendError import VmError, XendError
from xen.xend.XendLogging import log
from xen.xend.server.netif import randomMAC
from xen.xend.xenstore.xswatch import xswatch
@@ -152,13 +152,13 @@ class ImageHandler:
necessary."""
return mem_kb
- def getRequiredInitialReservation(self, 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(mem_kb)
+ return self.getRequiredAvailableMemory(self.vm.getMemoryTarget())
def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
"""@param shadow_mem_kb The configured shadow memory, in KiB.
@@ -189,7 +189,10 @@ class LinuxImageHandler(ImageHandler):
store_evtchn = self.vm.getStorePort()
console_evtchn = self.vm.getConsolePort()
- log.debug("dom = %d", self.vm.getDomid())
+ 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)
@@ -198,7 +201,8 @@ class LinuxImageHandler(ImageHandler):
log.debug("vcpus = %d", self.vm.getVCpuCount())
log.debug("features = %s", self.vm.getFeatures())
- return xc.linux_build(dom = self.vm.getDomid(),
+ return xc.linux_build(domid = self.vm.getDomid(),
+ memsize = mem_mb,
image = self.kernel,
store_evtchn = store_evtchn,
console_evtchn = console_evtchn,
@@ -218,7 +222,10 @@ class PPC_LinuxImageHandler(LinuxImageHandler):
store_evtchn = self.vm.getStorePort()
console_evtchn = self.vm.getConsolePort()
- log.debug("dom = %d", self.vm.getDomid())
+ 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)
@@ -229,7 +236,8 @@ class PPC_LinuxImageHandler(LinuxImageHandler):
devtree = FlatDeviceTree.build(self)
- return xc.linux_build(dom = self.vm.getDomid(),
+ return xc.linux_build(domid = self.vm.getDomid(),
+ memsize = mem_mb,
image = self.kernel,
store_evtchn = store_evtchn,
console_evtchn = console_evtchn,
@@ -240,6 +248,11 @@ class PPC_LinuxImageHandler(LinuxImageHandler):
class HVMImageHandler(ImageHandler):
+ def __init__(self, vm, imageConfig, deviceConfig):
+ ImageHandler.__init__(self, vm, imageConfig, deviceConfig)
+ self.shutdownWatch = None
+
+
def configure(self, imageConfig, deviceConfig):
ImageHandler.configure(self, imageConfig, deviceConfig)
@@ -267,30 +280,29 @@ class HVMImageHandler(ImageHandler):
self.pae = int(sxp.child_value(imageConfig, 'pae', 0))
self.acpi = int(sxp.child_value(imageConfig, 'acpi', 0))
- self.apic = int(sxp.child_value(imageConfig, 'apic', 0))
def buildDomain(self):
store_evtchn = self.vm.getStorePort()
- log.debug("dom = %d", self.vm.getDomid())
+ mem_mb = self.getRequiredInitialReservation() / 1024
+
+ log.debug("domid = %d", self.vm.getDomid())
log.debug("image = %s", self.kernel)
log.debug("store_evtchn = %d", store_evtchn)
- log.debug("memsize = %d", self.vm.getMemoryTarget() / 1024)
+ log.debug("memsize = %d", mem_mb)
log.debug("vcpus = %d", self.vm.getVCpuCount())
log.debug("pae = %d", self.pae)
log.debug("acpi = %d", self.acpi)
- log.debug("apic = %d", self.apic)
self.register_shutdown_watch()
- return xc.hvm_build(dom = self.vm.getDomid(),
+ return xc.hvm_build(domid = self.vm.getDomid(),
image = self.kernel,
store_evtchn = store_evtchn,
- memsize = self.vm.getMemoryTarget() / 1024,
+ memsize = mem_mb,
vcpus = self.vm.getVCpuCount(),
pae = self.pae,
- acpi = self.acpi,
- apic = self.apic)
+ acpi = self.acpi)
# Return a list of cmd line args to the device models based on the
# xm config file
@@ -361,9 +373,6 @@ class HVMImageHandler(ImageHandler):
if nographic:
ret.append('-nographic')
- # remove password
- if vncpasswd_vmconfig:
- config.remove(['vncpasswd', vncpasswd_vmconfig])
return ret
if vnc:
@@ -395,9 +404,6 @@ class HVMImageHandler(ImageHandler):
if vncpasswd != '':
self.vm.storeVm("vncpasswd", vncpasswd)
- # remove password
- config.remove(['vncpasswd', vncpasswd_vmconfig])
-
return ret
def createDeviceModel(self):
@@ -407,7 +413,7 @@ class HVMImageHandler(ImageHandler):
#todo: Error handling
args = [self.device_model]
args = args + ([ "-d", "%d" % self.vm.getDomid(),
- "-m", "%s" % (self.vm.getMemoryTarget() / 1024)])
+ "-m", "%s" % (self.getRequiredInitialReservation() / 1024)])
args = args + self.dmargs
env = dict(os.environ)
if self.display:
@@ -450,14 +456,18 @@ class HVMImageHandler(ImageHandler):
""" watch call back on node control/shutdown,
if node changed, this function will be called
"""
- from xen.xend.XendDomainInfo import shutdown_reasons
+ from xen.xend.XendConstants import DOMAIN_SHUTDOWN_REASONS
xd = xen.xend.XendDomain.instance()
- vm = xd.domain_lookup( self.vm.getDomid() )
+ try:
+ vm = xd.domain_lookup( self.vm.getDomid() )
+ except XendError:
+ # domain isn't registered, no need to clean it up.
+ return
- reason = vm.readDom('control/shutdown')
+ reason = vm.getShutdownReason()
log.debug("hvm_shutdown fired, shutdown reason=%s", reason)
- for x in shutdown_reasons.keys():
- if shutdown_reasons[x] == reason:
+ for x in DOMAIN_SHUTDOWN_REASONS.keys():
+ if DOMAIN_SHUTDOWN_REASONS[x] == reason:
vm.info['shutdown'] = 1
vm.info['shutdown_reason'] = x
vm.refreshShutdown(vm.info)
@@ -484,22 +494,12 @@ class X86_HVM_ImageHandler(HVMImageHandler):
def getRequiredAvailableMemory(self, mem_kb):
# Add 8 MiB overhead for QEMU's video RAM.
- return self.getRequiredInitialReservation(mem_kb) + 8192
-
- def getRequiredInitialReservation(self, mem_kb):
- page_kb = 4
- # This was derived emperically:
- # 2.4 MB overhead per 1024 MB RAM
- # + 4 to avoid low-memory condition
- extra_mb = (2.4/1024) * (mem_kb/1024.0) + 4;
- extra_pages = int( math.ceil( extra_mb*1024 / page_kb ))
- return mem_kb + extra_pages * page_kb
+ return mem_kb + 8192
- def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
- # The given value is the configured value -- we need to include the
- # overhead due to getRequiredInitialReservation.
- maxmem_kb = self.getRequiredInitialReservation(maxmem_kb)
+ def getRequiredInitialReservation(self):
+ return self.vm.getMemoryTarget()
+ 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.
diff --git a/tools/python/xen/xend/server/DevController.py b/tools/python/xen/xend/server/DevController.py
index fed36b40a1..cbe3b18845 100644
--- a/tools/python/xen/xend/server/DevController.py
+++ b/tools/python/xen/xend/server/DevController.py
@@ -88,9 +88,9 @@ class DevController:
xd = xen.xend.XendDomain.instance()
backdom_name = sxp.child_value(config, 'backend')
if backdom_name is None:
- backdom = xen.xend.XendDomain.PRIV_DOMAIN
+ backdom = xen.xend.XendDomain.DOM0_ID
else:
- bd = xd.domain_lookup_by_name_or_id_nr(backdom_name)
+ bd = xd.domain_lookup_nr(backdom_name)
backdom = bd.getDomid()
count = 0
while True:
@@ -141,7 +141,6 @@ class DevController:
def waitForDevices(self):
log.debug("Waiting for devices %s.", self.deviceClass)
-
return map(self.waitForDevice, self.deviceIDs())
@@ -221,13 +220,15 @@ class DevController:
"""@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."""
-
- backdomid = xstransact.Read(self.frontendPath(devid), "backend-id")
- if backdomid is None:
- raise VmError("Device %s not connected" % devid)
-
- return [self.deviceClass, ['backend', int(backdomid)]]
-
+ configDict = self.getDeviceConfiguration(devid)
+ sxpr = [self.deviceClass]
+ 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 sxprs(self):
"""@return an s-expression describing all the devices of this
@@ -243,6 +244,25 @@ class DevController:
'id', devid]]
+ def getDeviceConfiguration(self, devid):
+ """Returns the configuration of a device.
+
+ @note: Similar to L{configuration} except it returns a dict.
+ @return: dict
+ """
+ backdomid = xstransact.Read(self.frontendPath(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
+
## protected:
def getDeviceDetails(self, config):
@@ -387,7 +407,7 @@ class DevController:
backdom_name = sxp.child_value(config, 'backend')
if backdom_name:
- backdom = xd.domain_lookup_by_name_or_id_nr(backdom_name)
+ backdom = xd.domain_lookup_nr(backdom_name)
else:
backdom = xd.privilegedDomain()
@@ -435,7 +455,9 @@ class DevController:
def backendPath(self, backdom, devid):
- """@param backdom [XendDomainInfo] The backend domain info."""
+ """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,
@@ -450,10 +472,11 @@ class DevController:
return "%s/device/%s" % (self.vm.getDomainPath(), self.deviceClass)
def backendRoot(self):
- import xen.xend.XendDomain
- from xen.xend.xenstore.xsutil import GetDomainPath
- backdom = xen.xend.XendDomain.PRIV_DOMAIN
- return "%s/backend/%s/%s" % (GetDomainPath(backdom), self.deviceClass, self.vm.getDomid())
+ """Construct backend root path assuming backend is domain 0."""
+ from xen.xend.XendDomain import DOM0_ID
+ from xen.xend.xenstore.xsutil import GetDomainPath
+ return "%s/backend/%s/%s" % (GetDomainPath(DOM0_ID),
+ self.deviceClass, self.vm.getDomid())
def frontendMiscPath(self):
return "%s/device-misc/%s" % (self.vm.getDomainPath(),
diff --git a/tools/python/xen/xend/server/SrvDaemon.py b/tools/python/xen/xend/server/SrvDaemon.py
index baba3c437d..864c371552 100644
--- a/tools/python/xen/xend/server/SrvDaemon.py
+++ b/tools/python/xen/xend/server/SrvDaemon.py
@@ -205,6 +205,8 @@ class Daemon:
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
@@ -297,9 +299,17 @@ class Daemon:
log.info("Xend changeset: %s.", xinfo['xen_changeset'])
del xc
+ try:
+ from xen import VERSION
+ log.info("Xend version: %s", VERSION)
+ except ImportError:
+ log.info("Xend version: Unknown.")
+
relocate.listenRelocation()
servers = SrvServer.create()
servers.start(status)
+ del servers
+
except Exception, ex:
print >>sys.stderr, 'Exception starting xend:', ex
if XEND_DEBUG:
diff --git a/tools/python/xen/xend/server/SrvDomainDir.py b/tools/python/xen/xend/server/SrvDomainDir.py
index d0bf999261..fe97b29aef 100644
--- a/tools/python/xen/xend/server/SrvDomainDir.py
+++ b/tools/python/xen/xend/server/SrvDomainDir.py
@@ -39,7 +39,7 @@ class SrvDomainDir(SrvDir):
self.xd = XendDomain.instance()
def domain(self, x):
- dom = self.xd.domain_lookup_by_name_or_id(x)
+ dom = self.xd.domain_lookup(x)
if not dom:
raise XendError('No such domain ' + str(x))
return SrvDomain(dom)
diff --git a/tools/python/xen/xend/server/SrvServer.py b/tools/python/xen/xend/server/SrvServer.py
index 1826561db9..52532fbb29 100644
--- a/tools/python/xen/xend/server/SrvServer.py
+++ b/tools/python/xen/xend/server/SrvServer.py
@@ -42,6 +42,7 @@
import fcntl
import time
+import signal
from threading import Thread
from xen.web.httpserver import HttpServer, UnixHttpServer
@@ -54,7 +55,6 @@ from xen.web.SrvDir import SrvDir
from SrvRoot import SrvRoot
from XMLRPCServer import XMLRPCServer
-
xroot = XendRoot.instance()
@@ -66,6 +66,14 @@ class XendServers:
def add(self, server):
self.servers.append(server)
+ def cleanup(self, signum = 0, frame = None):
+ log.debug("SrvServer.cleanup()")
+ for server in self.servers:
+ try:
+ server.shutdown()
+ except:
+ pass
+
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
@@ -100,8 +108,24 @@ class XendServers:
status.write('0')
status.close()
- for t in threads:
- t.join()
+ # Prepare to catch SIGTERM (received when 'xend stop' is executed)
+ # and call each server's cleanup if possible
+ signal.signal(signal.SIGTERM, self.cleanup)
+
+ # Interruptible Thread.join - Python Bug #1167930
+ # Replaces: for t in threads: t.join()
+ # Reason: The above will cause python signal handlers to be
+ # blocked so we're not able to catch SIGTERM in any
+ # way for cleanup
+ runningThreads = len([t for t in threads if t.isAlive()])
+ while runningThreads > 0:
+ try:
+ for t in threads:
+ t.join(1.0)
+ runningThreads = len([t for t in threads if t.isAlive()])
+ except:
+ pass
+
def create():
root = SrvDir()
diff --git a/tools/python/xen/xend/server/XMLRPCServer.py b/tools/python/xen/xend/server/XMLRPCServer.py
index d075ec7e5e..4ab43ca7f9 100644
--- a/tools/python/xen/xend/server/XMLRPCServer.py
+++ b/tools/python/xen/xend/server/XMLRPCServer.py
@@ -16,49 +16,49 @@
# Copyright (C) 2006 XenSource Ltd.
#============================================================================
+import types
import xmlrpclib
-
-from xen.xend import XendDomain, XendDomainInfo, XendNode, \
- XendLogging, XendDmesg
from xen.util.xmlrpclib2 import UnixXMLRPCServer, TCPXMLRPCServer
-from xen.xend.XendClient import XML_RPC_SOCKET, ERROR_INVALID_DOMAIN
-from xen.xend.XendError import *
+from xen.xend import XendDomain, XendDomainInfo, XendNode
+from xen.xend import XendLogging, XendDmesg
+from xen.xend.XendClient import XML_RPC_SOCKET
from xen.xend.XendLogging import log
-from types import ListType
-
-def lookup(domid):
- info = XendDomain.instance().domain_lookup_by_name_or_id(domid)
- if not info:
- raise XendInvalidDomain(str(domid))
- return info
-
-def dispatch(domid, fn, args):
- info = lookup(domid)
- return getattr(info, fn)(*args)
+from xen.xend.XendAPI import XendAPI
+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) is ListType:
+ if type(k) in (types.ListType, types.TupleType):
if len(k) != 2 or k[0] != 'vcpu_avail':
ret.append(fixup_sxpr(k))
else:
ret.append(k)
return ret
-def domain(domid):
+def lookup(domid):
+ info = XendDomain.instance().domain_lookup(domid)
+ if not info:
+ raise XendInvalidDomain(str(domid))
+ return info
+
+def dispatch(domid, fn, args):
info = lookup(domid)
- return fixup_sxpr(info.sxpr())
+ return getattr(info, fn)(*args)
+
+def domain(domid, full = 0):
+ info = lookup(domid)
+ return fixup_sxpr(info.sxpr(not full))
-def domains(detail=1):
+def domains(detail=1, full = 0):
if detail < 1:
return XendDomain.instance().list_names()
else:
domains = XendDomain.instance().list_sorted()
- return map(lambda dom: fixup_sxpr(dom.sxpr()), domains)
+ return map(lambda dom: fixup_sxpr(dom.sxpr(not full)), domains)
def domain_create(config):
info = XendDomain.instance().domain_create(config)
@@ -75,8 +75,8 @@ def get_log():
finally:
f.close()
-methods = ['device_create', 'device_configure', 'destroyDevice',
- 'getDeviceSxprs',
+methods = ['device_create', 'device_configure',
+ 'destroyDevice','getDeviceSxprs',
'setMemoryTarget', 'setName', 'setVCpuCount', 'shutdown',
'send_sysrq', 'getVCPUInfo', 'waitForDevices',
'getRestartCount']
@@ -84,25 +84,44 @@ methods = ['device_create', 'device_configure', 'destroyDevice',
exclude = ['domain_create', 'domain_restore']
class XMLRPCServer:
- def __init__(self, use_tcp=False):
- self.ready = False
+ def __init__(self, use_tcp=False, host = "localhost", port = 8006,
+ path = XML_RPC_SOCKET):
self.use_tcp = use_tcp
+ self.port = port
+ self.host = host
+ self.path = path
+
+ self.ready = False
+ self.running = True
+ self.xenapi = XendAPI()
def run(self):
if self.use_tcp:
- # bind to something fixed for now as we may eliminate
- # tcp support completely.
- self.server = TCPXMLRPCServer(("localhost", 8005), logRequests=False)
+ self.server = TCPXMLRPCServer((self.host, self.port),
+ logRequests = False)
else:
- self.server = UnixXMLRPCServer(XML_RPC_SOCKET, False)
+ self.server = UnixXMLRPCServer(self.path, logRequests = False)
+
+ # 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):
+ meth = getattr(self.xenapi, meth_name)
+ if meth_name[0] != '_' and callable(meth) and hasattr(meth, 'api'):
+ self.server.register_function(meth, getattr(meth, 'api'))
+
+ # 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)
- # Functions in XendDomain
inst = XendDomain.instance()
+
for name in dir(inst):
fn = getattr(inst, name)
if name.startswith("domain_") and callable(fn):
@@ -126,4 +145,20 @@ class XMLRPCServer:
self.server.register_introspection_functions()
self.ready = True
- self.server.serve_forever()
+
+ # Custom runloop so we can cleanup when exiting.
+ # -----------------------------------------------------------------
+ try:
+ self.server.socket.settimeout(1.0)
+ while self.running:
+ self.server.handle_request()
+ finally:
+ self.cleanup()
+
+ def cleanup(self):
+ log.debug("XMLRPCServer.cleanup()")
+
+ def shutdown(self):
+ self.running = False
+ self.ready = False
+
diff --git a/tools/python/xen/xend/server/blkif.py b/tools/python/xen/xend/server/blkif.py
index 9726b43868..6bd49acfc9 100644
--- a/tools/python/xen/xend/server/blkif.py
+++ b/tools/python/xen/xend/server/blkif.py
@@ -16,7 +16,6 @@
# Copyright (C) 2005, 2006 XenSource Inc.
#============================================================================
-
import re
import string
@@ -24,10 +23,8 @@ from xen.util import blkif
from xen.util import security
from xen.xend import sxp
from xen.xend.XendError import VmError
-
from xen.xend.server.DevController import DevController
-
class BlkifController(DevController):
"""Block device interface controller. Handles all block devices
for a domain.
@@ -38,12 +35,10 @@ class BlkifController(DevController):
"""
DevController.__init__(self, vm)
-
def getDeviceDetails(self, config):
"""@see DevController.getDeviceDetails"""
- uname = sxp.child_value(config, 'uname')
-
- dev = sxp.child_value(config, 'dev')
+ uname = sxp.child_value(config, 'uname', '')
+ dev = sxp.child_value(config, 'dev', '')
if 'ioemu:' in dev:
(_, dev) = string.split(dev, ':', 1)
@@ -74,6 +69,10 @@ class BlkifController(DevController):
'mode' : mode
}
+ uuid = sxp.child_value(config, 'uuid')
+ if uuid:
+ back['uuid'] = uuid
+
if security.on():
(label, ssidref, policy) = security.get_res_security_details(uname)
back.update({'acm_label' : label,
@@ -81,7 +80,7 @@ class BlkifController(DevController):
'acm_policy' : policy})
devid = blkif.blkdev_name_to_number(dev)
- if not devid:
+ if devid is None:
raise VmError('Unable to find number for device (%s)' % (dev))
front = { 'virtual-device' : "%i" % devid,
@@ -108,27 +107,30 @@ class BlkifController(DevController):
(self.deviceClass, devid, config))
- def configuration(self, devid):
- """@see DevController.configuration"""
-
- result = DevController.configuration(self, devid)
-
- (dev, typ, params, mode) = self.readBackend(devid,
- 'dev', 'type', 'params',
- 'mode')
+ def getDeviceConfiguration(self, devid):
+ """Returns the configuration of a device.
+ @note: Similar to L{configuration} except it returns a dict.
+ @return: dict
+ """
+ config = DevController.getDeviceConfiguration(self, devid)
+ devinfo = self.readBackend(devid, 'dev', 'type', 'params', 'mode',
+ 'uuid')
+ dev, typ, params, mode, uuid = devinfo
+
if dev:
- (dev_type) = self.readFrontend(devid, 'device-type')
+ dev_type = self.readFrontend(devid, 'device-type')
if dev_type:
- dev += ":" + dev_type
- result.append(['dev', dev])
+ dev += ':' + dev_type
+ config['dev'] = dev
if typ and params:
- result.append(['uname', typ + ":" + params])
+ config['uname'] = typ +':' + params
if mode:
- result.append(['mode', mode])
-
- return result
+ config['mode'] = mode
+ if uuid:
+ config['uuid'] = uuid
+ return config
def destroyDevice(self, devid):
"""@see DevController.destroyDevice"""
diff --git a/tools/python/xen/xend/server/iopif.py b/tools/python/xen/xend/server/iopif.py
index 67575f2158..96651a7c95 100644
--- a/tools/python/xen/xend/server/iopif.py
+++ b/tools/python/xen/xend/server/iopif.py
@@ -20,7 +20,7 @@
import types
-import xen.lowlevel.xc;
+import xen.lowlevel.xc
from xen.xend import sxp
from xen.xend.XendError import VmError
@@ -72,7 +72,7 @@ class IOPortsController(DevController):
raise VmError('ioports: Invalid i/o range: %s - %s' %
(io_from, io_to))
- rc = xc.domain_ioport_permission(dom = self.getDomid(),
+ rc = xc.domain_ioport_permission(domid = self.getDomid(),
first_port = io_from,
nr_ports = io_to - io_from + 1,
allow_access = True)
diff --git a/tools/python/xen/xend/server/irqif.py b/tools/python/xen/xend/server/irqif.py
index 98a2d21a00..6e539dbab9 100644
--- a/tools/python/xen/xend/server/irqif.py
+++ b/tools/python/xen/xend/server/irqif.py
@@ -23,7 +23,7 @@
import types
-import xen.lowlevel.xc;
+import xen.lowlevel.xc
from xen.xend import sxp
from xen.xend.XendError import VmError
diff --git a/tools/python/xen/xend/server/netif.py b/tools/python/xen/xend/server/netif.py
index 94016e3628..4c7e359617 100644
--- a/tools/python/xen/xend/server/netif.py
+++ b/tools/python/xen/xend/server/netif.py
@@ -26,13 +26,10 @@ import re
from xen.xend import sxp
from xen.xend import XendRoot
-
from xen.xend.server.DevController import DevController
-
xroot = XendRoot.instance()
-
def randomMAC():
"""Generate a random MAC address.
@@ -139,7 +136,6 @@ class NetifController(DevController):
def __init__(self, vm):
DevController.__init__(self, vm)
-
def getDeviceDetails(self, config):
"""@see DevController.getDeviceDetails"""
@@ -157,6 +153,7 @@ class NetifController(DevController):
mac = sxp.child_value(config, 'mac')
vifname = sxp.child_value(config, 'vifname')
rate = sxp.child_value(config, 'rate')
+ uuid = sxp.child_value(config, 'uuid')
ipaddr = _get_config_ipaddr(config)
devid = self.allocateDeviceID()
@@ -182,34 +179,37 @@ class NetifController(DevController):
back['vifname'] = vifname
if rate:
back['rate'] = parseRate(rate)
+ if uuid:
+ back['uuid'] = uuid
return (devid, back, front)
- def configuration(self, devid):
+ def getDeviceConfiguration(self, devid):
"""@see DevController.configuration"""
- result = DevController.configuration(self, devid)
-
- (script, ip, bridge, mac, typ, vifname, rate) = self.readBackend(
- devid, 'script', 'ip', 'bridge', 'mac', 'type', 'vifname', 'rate')
+ result = DevController.getDeviceConfiguration(self, devid)
+ devinfo = self.readBackend(devid, 'script', 'ip', 'bridge',
+ 'mac', 'type', 'vifname', 'rate', 'uuid')
+ (script, ip, bridge, mac, typ, vifname, rate, uuid) = devinfo
if script:
- result.append(['script',
- script.replace(xroot.network_script_dir + os.sep,
- "")])
+ network_script_dir = xroot.network_script_dir + os.sep
+ result['script'] = script.replace(network_script_dir, "")
if ip:
- for i in ip.split(" "):
- result.append(['ip', i])
+ result['ip'] = ip.split(" ")
if bridge:
- result.append(['bridge', bridge])
+ result['bridge'] = bridge
if mac:
- result.append(['mac', mac])
+ result['mac'] = mac
if typ:
- result.append(['type', typ])
+ result['type'] = typ
if vifname:
- result.append(['vifname', vifname])
+ result['vifname'] = vifname
if rate:
- result.append(['rate', formatRate(rate)])
+ result['rate'] = formatRate(rate)
+ if uuid:
+ result['uuid'] = uuid
return result
+
diff --git a/tools/python/xen/xend/server/pciif.py b/tools/python/xen/xend/server/pciif.py
index c6ffcd55e7..aa7f3c8c98 100644
--- a/tools/python/xen/xend/server/pciif.py
+++ b/tools/python/xen/xend/server/pciif.py
@@ -109,30 +109,55 @@ class PciController(DevController):
return (0, back, {})
- def configuration(self, devid):
- """@see DevController.configuration"""
-
- result = DevController.configuration(self, devid)
-
- (num_devs) = self.readBackend(devid, 'num_devs')
-
+ def getDeviceConfiguration(self, devid):
+ result = DevController.getDeviceConfiguration(self, devid)
+ num_devs = self.readBackend(devid, 'num_devs')
+ pci_devs = []
+
for i in range(int(num_devs)):
- (dev_config) = self.readBackend(devid, 'dev-%d'%(i))
+ (dev_config,) = self.readBackend(devid, 'dev-%d'%(i))
pci_match = re.match(r"((?P<domain>[0-9a-fA-F]{1,4})[:,])?" + \
r"(?P<bus>[0-9a-fA-F]{1,2})[:,]" + \
r"(?P<slot>[0-9a-fA-F]{1,2})[.,]" + \
r"(?P<func>[0-9a-fA-F]{1,2})", dev_config)
+
if pci_match!=None:
pci_dev_info = pci_match.groupdict('0')
- result.append( ['dev', \
- ['domain', '0x'+pci_dev_info['domain']], \
- ['bus', '0x'+pci_dev_info['bus']], \
- ['slot', '0x'+pci_dev_info['slot']], \
- ['func', '0x'+pci_dev_info['func']]])
+ pci_devs.append({'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})
+ result['dev'] = pci_devs
return result
+ def configuration(self, devid):
+ """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)
+ sxpr = [self.deviceClass]
+
+ # remove devs
+ devs = configDict.pop('dev', [])
+ 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 setupDevice(self, domain, bus, slot, func):
""" Attach I/O resources for device to frontend domain
"""
@@ -155,9 +180,9 @@ class PciController(DevController):
PCIQuirk(dev.vendor, dev.device, dev.subvendor, dev.subdevice, domain,
bus, slot, func)
- for (start, size) in dev.ioports:
+ for (start, size) in dev.ioports:
log.debug('pci: enabling ioport 0x%x/0x%x'%(start,size))
- rc = xc.domain_ioport_permission(dom = fe_domid, first_port = start,
+ 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 '+
@@ -171,7 +196,7 @@ class PciController(DevController):
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(dom = fe_domid,
+ rc = xc.domain_iomem_permission(domid = fe_domid,
first_pfn = start_pfn,
nr_pfns = nr_pfns,
allow_access = True)
@@ -181,7 +206,7 @@ class PciController(DevController):
if dev.irq>0:
log.debug('pci: enabling irq %d'%dev.irq)
- rc = xc.domain_irq_permission(dom = fe_domid, pirq = 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 '+
diff --git a/tools/python/xen/xend/server/tpmif.py b/tools/python/xen/xend/server/tpmif.py
index 71ec6e15cb..c313efe2a8 100644
--- a/tools/python/xen/xend/server/tpmif.py
+++ b/tools/python/xen/xend/server/tpmif.py
@@ -22,20 +22,17 @@
"""
from xen.xend import sxp
+from xen.xend import XendRoot
from xen.xend.XendLogging import log
from xen.xend.XendError import XendError
-from xen.xend import XendRoot
-from xen.xend.XendDomainInfo import DEV_MIGRATE_TEST
-
+from xen.xend.XendConstants import DEV_MIGRATE_TEST
from xen.xend.server.DevController import DevController
import os
import re
-
xroot = XendRoot.instance()
-
class TPMifController(DevController):
"""TPM interface controller. Handles all TPM devices for a domain.
"""
@@ -52,22 +49,37 @@ class TPMifController(DevController):
if inst == -1:
inst = int(sxp.child_value(config, 'instance' , '0'))
+ typ = sxp.child_value(config, 'type')
+ uuid = sxp.child_value(config, '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
+
front = { 'handle' : "%i" % devid }
return (devid, back, front)
- def configuration(self, devid):
-
- result = DevController.configuration(self, devid)
+ def getDeviceConfiguration(self, devid):
+ """Returns the configuration of a device"""
+ result = DevController.getDeviceConfiguration(self, devid)
- instance = self.readBackend(devid, 'instance')
+ (instance, uuid, type) = \
+ self.readBackend(devid, 'instance',
+ 'uuid',
+ 'type')
if instance:
- result.append(['instance', instance])
+ result['instance'] = instance
+ if uuid:
+ result['uuid'] = uuid
+ if type:
+ result['type'] == type
return result
diff --git a/tools/python/xen/xend/sxp.py b/tools/python/xen/xend/sxp.py
index e37adfb119..a9e9adf17f 100644
--- a/tools/python/xen/xend/sxp.py
+++ b/tools/python/xen/xend/sxp.py
@@ -267,10 +267,14 @@ class Parser:
elif c == 'x':
self.state.fn = self.state_hex
self.state.val = 0
- else:
+ 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):
@@ -375,7 +379,7 @@ def atomp(sxpr):
def show(sxpr, out=sys.stdout):
"""Print an sxpr in bracketed (lisp-style) syntax.
"""
- if isinstance(sxpr, types.ListType):
+ if isinstance(sxpr, (types.ListType, types.TupleType)):
out.write(k_list_open)
i = 0
for x in sxpr:
@@ -393,7 +397,7 @@ def show(sxpr, out=sys.stdout):
def show_xml(sxpr, out=sys.stdout):
"""Print an sxpr in XML syntax.
"""
- if isinstance(sxpr, types.ListType):
+ if isinstance(sxpr, (types.ListType, types.TupleType)):
element = name(sxpr)
out.write('<%s' % element)
for attr in attributes(sxpr):
@@ -416,7 +420,7 @@ def elementp(sxpr, elt=None):
sxpr sxpr
elt element type
"""
- return (isinstance(sxpr, types.ListType)
+ return (isinstance(sxpr, (types.ListType, types.TupleType))
and len(sxpr)
and (None == elt or sxpr[0] == elt))
@@ -432,7 +436,7 @@ def name(sxpr):
val = None
if isinstance(sxpr, types.StringType):
val = sxpr
- elif isinstance(sxpr, types.ListType) and len(sxpr):
+ elif isinstance(sxpr, (types.ListType, types.TupleType)) and len(sxpr):
val = sxpr[0]
return val
@@ -444,7 +448,7 @@ def attributes(sxpr):
returns attribute list
"""
val = []
- if isinstance(sxpr, types.ListType) and len(sxpr) > 1:
+ if isinstance(sxpr, (types.ListType, types.TupleType)) and len(sxpr) > 1:
attr = sxpr[1]
if elementp(attr, k_attr_open):
val = attr[1:]
@@ -474,7 +478,7 @@ def children(sxpr, elt=None):
returns children (filtered by elt if specified)
"""
val = []
- if isinstance(sxpr, types.ListType) and len(sxpr) > 1:
+ if isinstance(sxpr, (types.ListType, types.TupleType)) and len(sxpr) > 1:
i = 1
x = sxpr[i]
if elementp(x, k_attr_open):
@@ -563,7 +567,7 @@ def with_id(sxpr, id, val=None):
return s-exp or val
"""
- if isinstance(sxpr, types.ListType):
+ if isinstance(sxpr, (types.ListType, types.TupleType)):
for n in sxpr:
if has_id(n, id):
val = n
@@ -583,7 +587,7 @@ def child_with_id(sxpr, id, val=None):
return s-exp or val
"""
- if isinstance(sxpr, types.ListType):
+ if isinstance(sxpr, (types.ListType, types.TupleType)):
for n in sxpr:
if has_id(n, id):
val = n
@@ -605,7 +609,7 @@ def elements(sxpr, ctxt=None):
yield (sxpr, ctxt)
i = 0
for n in children(sxpr):
- if isinstance(n, types.ListType):
+ 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)):
diff --git a/tools/python/xen/xend/uuid.py b/tools/python/xen/xend/uuid.py
index 401540bc6e..7fe52cb335 100644
--- a/tools/python/xen/xend/uuid.py
+++ b/tools/python/xen/xend/uuid.py
@@ -54,10 +54,6 @@ def getUuidRandom():
uuidFactory = getUuidRandom
-def create():
- return uuidFactory()
-
-
def toString(u):
return "-".join(["%02x" * 4, "%02x" * 2, "%02x" * 2, "%02x" * 2,
"%02x" * 6]) % tuple(u)
@@ -65,3 +61,9 @@ def toString(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/xm/create.py b/tools/python/xen/xm/create.py
index fa1f23f771..d94356d5c5 100644
--- a/tools/python/xen/xm/create.py
+++ b/tools/python/xen/xm/create.py
@@ -181,10 +181,6 @@ gopts.var('acpi', val='ACPI',
fn=set_int, default=0,
use="Disable or enable ACPI of HVM domain.")
-gopts.var('apic', val='APIC',
- fn=set_int, default=0,
- use="Disable or enable APIC of HVM domain.")
-
gopts.var('vcpus', val='VCPUS',
fn=set_int, default=1,
use="# of Virtual CPUS in domain.")
@@ -299,7 +295,7 @@ gopts.var('vif', val="type=TYPE,mac=MAC,bridge=BRIDGE,ip=IPADDR,script=SCRIPT,ba
This option may be repeated to add more than one vif.
Specifying vifs will increase the number of interfaces as needed.""")
-gopts.var('vtpm', val="instance=INSTANCE,backend=DOM",
+gopts.var('vtpm', val="instance=INSTANCE,backend=DOM,type=TYPE",
fn=append_value, default=[],
use="""Add a TPM interface. On the backend side use the given
instance as virtual TPM instance. The given number is merely the
@@ -307,7 +303,11 @@ gopts.var('vtpm', val="instance=INSTANCE,backend=DOM",
which instance number will actually be assigned to the domain.
The associtation between virtual machine and the TPM instance
number can be found in /etc/xen/vtpm.db. Use the backend in the
- given domain.""")
+ given domain.
+ The type parameter can be used to select a specific driver type
+ that the VM can use. To prevent a fully virtualized domain (HVM)
+ from being able to access an emulated device model, you may specify
+ 'paravirtualized' here.""")
gopts.var('access_control', val="policy=POLICY,label=LABEL",
fn=append_value, default=[],
@@ -451,6 +451,18 @@ gopts.var('uuid', val='',
addresses for virtual network interfaces. This must be a unique
value across the entire cluster.""")
+gopts.var('on_xend_start', val='ignore|start',
+ fn=set_value, default='ignore',
+ use='Action to perform when xend starts')
+
+gopts.var('on_xend_stop', val='continue|shutdown|suspend',
+ fn=set_value, default="ignore",
+ use="""Behaviour when Xend stops:
+ - ignore: Domain continues to run;
+ - shutdown: Domain is shutdown;
+ - suspend: Domain is suspended;
+ """)
+
def err(msg):
"""Print an error to stderr and exit.
"""
@@ -585,27 +597,28 @@ def configure_vtpm(config_devs, vals):
"""Create the config for virtual TPM interfaces.
"""
vtpm = vals.vtpm
- vtpm_n = 1
- for idx in range(0, vtpm_n):
- if idx < len(vtpm):
- d = vtpm[idx]
- instance = d.get('instance')
- if instance == "VTPMD":
- instance = "0"
- else:
- if instance != None:
- try:
- if int(instance) == 0:
- err('VM config error: vTPM instance must not be 0.')
- except ValueError:
- err('Vm config error: could not parse instance number.')
- backend = d.get('backend')
- config_vtpm = ['vtpm']
- if instance:
- config_vtpm.append(['pref_instance', instance])
- if backend:
- config_vtpm.append(['backend', backend])
- config_devs.append(['device', config_vtpm])
+ if len(vtpm) > 0:
+ d = vtpm[0]
+ instance = d.get('instance')
+ if instance == "VTPMD":
+ instance = "0"
+ else:
+ if instance != None:
+ try:
+ if int(instance) == 0:
+ err('VM config error: vTPM instance must not be 0.')
+ except ValueError:
+ err('Vm config error: could not parse instance number.')
+ backend = d.get('backend')
+ typ = d.get('type')
+ config_vtpm = ['vtpm']
+ if instance:
+ config_vtpm.append(['pref_instance', instance])
+ if backend:
+ config_vtpm.append(['backend', backend])
+ if typ:
+ config_vtpm.append(['type', type])
+ config_devs.append(['device', config_vtpm])
def configure_vifs(config_devs, vals):
@@ -647,7 +660,7 @@ def configure_hvm(config_image, vals):
'localtime', 'serial', 'stdvga', 'isa', 'nographic', 'soundhw',
'vnc', 'vncdisplay', 'vncunused', 'vncconsole', 'vnclisten',
'sdl', 'display', 'xauthority',
- 'acpi', 'apic', 'usb', 'usbdevice' ]
+ 'acpi', 'usb', 'usbdevice' ]
for a in args:
if (vals.__dict__[a]):
config_image.append([a, vals.__dict__[a]])
@@ -682,8 +695,9 @@ def make_config(vals):
config.append([n, v])
map(add_conf, ['name', 'memory', 'maxmem', 'shadow_memory',
- 'restart', 'on_poweroff', 'on_reboot', 'on_crash',
- 'vcpus', 'features'])
+ 'restart', 'on_poweroff',
+ 'on_reboot', 'on_crash', 'vcpus', 'features',
+ 'on_xend_start', 'on_xend_stop'])
if vals.uuid is not None:
config.append(['uuid', vals.uuid])
@@ -1151,8 +1165,11 @@ def config_security_check(config, verbose):
(res_label, res_policy) = security.get_res_label(resource)
if not res_label:
res_label = ""
- print " --> res:"+res_label+" ("+res_policy+")"
- print " --> dom:"+domain_label+" ("+domain_policy+")"
+ print " --> res: %s (%s)" % (str(res_label),
+ str(res_policy))
+ print " --> dom: %s (%s)" % (str(domain_label),
+ str(domain_policy))
+
answer = 0
return answer
@@ -1183,7 +1200,10 @@ def main(argv):
return
if type(config) == str:
+ try:
config = sxp.parse(file(config))[0]
+ except IOError, exn:
+ raise OptionError("Cannot read file %s: %s" % (config, exn[1]))
if opts.vals.dryrun:
PrettyPrint.prettyprint(config)
diff --git a/tools/python/xen/xm/main.py b/tools/python/xen/xm/main.py
index 5239ae4156..d9c0a70024 100644
--- a/tools/python/xen/xm/main.py
+++ b/tools/python/xen/xm/main.py
@@ -39,6 +39,7 @@ from xen.xend import PrettyPrint
from xen.xend import sxp
from xen.xend import XendClient
from xen.xend.XendClient import server
+from xen.xend.XendConstants import *
from xen.xm.opts import OptionError, Opts, wrap, set_true
from xen.xm import console
@@ -92,6 +93,15 @@ SUBCOMMAND_HELP = {
'unpause' : ('<Domain>', 'Unpause a paused domain.'),
'uptime' : ('[-s] <Domain>', 'Print uptime for a domain.'),
+ # Life cycle xm commands
+ 'new' : ('<ConfigFile> [options] [vars]',
+ 'Adds a domain to Xend domain management'),
+ 'delete' : ('<DomainName>',
+ 'Remove a domain from Xend domain management.'),
+ 'start' : ('<DomainName>', 'Start a Xend managed domain'),
+ 'resume' : ('<DomainName>', 'Resume a Xend managed domain'),
+ 'suspend' : ('<DomainName>', 'Suspend a Xend maanged domain'),
+
# less used commands
'dmesg' : ('[-c|--clear]',
@@ -194,6 +204,8 @@ SUBCOMMAND_OPTIONS = {
common_commands = [
"console",
"create",
+ "new",
+ "delete",
"destroy",
"dump-core",
"help",
@@ -203,8 +215,11 @@ common_commands = [
"pause",
"reboot",
"restore",
+ "resume",
"save",
"shutdown",
+ "start",
+ "suspend",
"top",
"unpause",
"uptime",
@@ -214,6 +229,8 @@ common_commands = [
domain_commands = [
"console",
"create",
+ "new",
+ "delete",
"destroy",
"domid",
"domname",
@@ -226,8 +243,11 @@ domain_commands = [
"reboot",
"rename",
"restore",
+ "resume",
"save",
"shutdown",
+ "start",
+ "suspend",
"sysrq",
"top",
"unpause",
@@ -469,9 +489,9 @@ def xm_restore(args):
def getDomains(domain_names, full = 0):
if domain_names:
- return [server.xend.domain(dom) for dom in domain_names]
+ return [server.xend.domain(dom, full) for dom in domain_names]
else:
- return server.xend.domains(1)
+ return server.xend.domains(1, full)
def xm_list(args):
@@ -513,13 +533,16 @@ def xm_list(args):
def parse_doms_info(info):
def get_info(n, t, d):
return t(sxp.child_value(info, n, d))
+
+ def get_status(n, t, d):
+ return DOM_STATES[t(sxp.child_value(info, n, d))]
return {
'domid' : get_info('domid', int, -1),
'name' : get_info('name', str, '??'),
- 'mem' : get_info('memory', int, 0),
+ 'mem' : get_info('memory_dynamic_max', int, 0),
'vcpus' : get_info('online_vcpus', int, 0),
- 'state' : get_info('state', str, '??'),
+ 'state' : get_info('state', str, ''),
'cpu_time' : get_info('cpu_time', float, 0),
'up_time' : get_info('up_time', float, -1),
'seclabel' : security.get_security_printlabel(info),
@@ -531,7 +554,7 @@ def parse_sedf_info(info):
return t(sxp.child_value(info, n, d))
return {
- 'domid' : get_info('domain', int, -1),
+ 'domid' : get_info('domid', int, -1),
'period' : get_info('period', int, -1),
'slice' : get_info('slice', int, -1),
'latency' : get_info('latency', int, -1),
@@ -540,10 +563,10 @@ def parse_sedf_info(info):
}
def xm_brief_list(doms):
- print '%-40s %3s %8s %5s %5s %9s' % \
- ('Name', 'ID', 'Mem(MiB)', 'VCPUs', 'State', 'Time(s)')
+ print '%-40s %3s %5s %5s %10s %9s' % \
+ ('Name', 'ID', 'Mem', 'VCPUs', 'State', 'Time(s)')
- format = "%(name)-40s %(domid)3d %(mem)8d %(vcpus)5d %(state)5s " \
+ format = "%(name)-40s %(domid)3d %(mem)5d %(vcpus)5d %(state)10s " \
"%(cpu_time)8.1f"
for dom in doms:
@@ -551,11 +574,11 @@ def xm_brief_list(doms):
print format % d
def xm_label_list(doms):
- print '%-32s %3s %8s %5s %5s %9s %-8s' % \
- ('Name', 'ID', 'Mem(MiB)', 'VCPUs', 'State', 'Time(s)', 'Label')
+ print '%-32s %3s %5s %5s %5s %9s %-8s' % \
+ ('Name', 'ID', 'Mem', 'VCPUs', 'State', 'Time(s)', 'Label')
output = []
- format = '%(name)-32s %(domid)3d %(mem)8d %(vcpus)5d %(state)5s ' \
+ format = '%(name)-32s %(domid)3d %(mem)5d %(vcpus)5d %(state)10s ' \
'%(cpu_time)8.1f %(seclabel)9s'
for dom in doms:
@@ -683,6 +706,26 @@ def xm_vcpu_list(args):
print format % locals()
+def xm_start(args):
+ arg_check(args, "start", 1)
+ dom = args[0]
+ server.xend.domain.start(dom)
+
+def xm_delete(args):
+ arg_check(args, "delete", 1)
+ dom = args[0]
+ server.xend.domain.delete(dom)
+
+def xm_suspend(args):
+ arg_check(args, "suspend", 1)
+ dom = args[0]
+ server.xend.domain.suspend(dom)
+
+def xm_resume(args):
+ arg_check(args, "resume", 1)
+ dom = args[0]
+ server.xend.domain.resume(dom)
+
def xm_reboot(args):
arg_check(args, "reboot", 1, 3)
from xen.xm import shutdown
@@ -1032,26 +1075,23 @@ def xm_top(args):
def xm_dmesg(args):
arg_check(args, "dmesg", 0, 1)
- gopts = Opts(use="""[-c|--clear]
-
-Read Xen's message buffer (boot output, warning and error messages) or clear
-its contents if the [-c|--clear] flag is specified.
-""")
-
- gopts.opt('clear', short='c',
- fn=set_true, default=0,
- use="Clear the contents of the Xen message buffer.")
- # Work around for gopts
- myargs = args
- myargs.insert(0, 'dmesg')
- gopts.parse(myargs)
+ try:
+ (options, params) = getopt.gnu_getopt(args, 'c', ['clear'])
+ except getopt.GetoptError, opterr:
+ err(opterr)
+ sys.exit(1)
- if len(myargs) not in (1, 2):
- err('Invalid arguments: ' + str(myargs))
+ use_clear = 0
+ for (k, v) in options:
+ if k in ['-c', '--clear']:
+ use_clear = 1
+
+ if len(params) :
+ err("No parameter required")
usage('dmesg')
sys.exit(1)
- if not gopts.vals.clear:
+ if not use_clear:
print server.xend.node.dmesg.info()
else:
server.xend.node.dmesg.clear()
@@ -1323,6 +1363,7 @@ commands = {
# xenstat commands
"top": xm_top,
# domain commands
+ "delete": xm_delete,
"destroy": xm_destroy,
"domid": xm_domid,
"domname": xm_domname,
@@ -1332,8 +1373,10 @@ commands = {
"restore": xm_restore,
"save": xm_save,
"shutdown": xm_shutdown,
+ "start": xm_start,
"sysrq": xm_sysrq,
"uptime": xm_uptime,
+ "suspend": xm_suspend,
"list": xm_list,
# memory commands
"mem-max": xm_mem_max,
@@ -1373,13 +1416,14 @@ commands = {
## The commands supported by a separate argument parser in xend.xm.
IMPORTED_COMMANDS = [
'create',
+ 'new',
'migrate',
'labels',
- 'addlabel',
'cfgbootpolicy',
'makepolicy',
'loadpolicy',
- 'dumppolicy',
+ 'dumppolicy',
+ 'addlabel',
'rmlabel',
'getlabel',
'dry-run',
diff --git a/tools/python/xen/xm/new.py b/tools/python/xen/xm/new.py
new file mode 100644
index 0000000000..fec2fc76e6
--- /dev/null
+++ b/tools/python/xen/xm/new.py
@@ -0,0 +1,68 @@
+#============================================================================
+# 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 os
+import xmlrpclib
+
+from xen.xend import PrettyPrint
+from xen.xend import sxp
+from xen.xend import XendClient
+from xen.xend.XendClient import server
+
+from xen.xm.opts import *
+from xen.xm.create import *
+
+def make_unstarted_domain(opts, config):
+ """Create an unstarted domain.
+
+ @param opts: options
+ @param config: configuration
+ """
+ try:
+ server.xend.domain.new(config)
+ except xmlrpclib.Fault, ex:
+ import signal
+ if vncpid:
+ os.kill(vncpid, signal.SIGKILL)
+ if ex.faultCode == XendClient.ERROR_INVALID_DOMAIN:
+ err("the domain '%s' does not exist." % ex.faultString)
+ else:
+ err("%s" % ex.faultString)
+ except Exception, ex:
+ import signal
+ if vncpid:
+ os.kill(vncpid, signal.SIGKILL)
+ err(str(ex))
+
+
+def main(argv):
+ try:
+ (opts, config) = parseCommandLine(argv)
+ except StandardError, ex:
+ err(str(ex))
+
+ if not opts:
+ return
+
+ if opts.vals.dryrun:
+ PrettyPrint.prettyprint(config)
+ else:
+ make_unstarted_domain(opts, config)
+
+if __name__ == '__main__':
+ main(sys.argv)
+
diff --git a/tools/xenmon/Makefile b/tools/xenmon/Makefile
index 725567d619..c1f4cd3f46 100644
--- a/tools/xenmon/Makefile
+++ b/tools/xenmon/Makefile
@@ -15,7 +15,7 @@ include $(XEN_ROOT)/tools/Rules.mk
sbindir=/usr/sbin
-CFLAGS += -Werror -g
+CFLAGS += -Werror
CFLAGS += -I $(XEN_XC)
CFLAGS += -I $(XEN_LIBXC)
LDFLAGS += -L $(XEN_LIBXC)
diff --git a/tools/xenmon/xenmon.py b/tools/xenmon/xenmon.py
index e6b8ce9e24..d828834a10 100644
--- a/tools/xenmon/xenmon.py
+++ b/tools/xenmon/xenmon.py
@@ -653,7 +653,6 @@ def writelog():
# start xenbaked
def start_xenbaked():
global options
- global args
os.system("killall -9 xenbaked")
# assumes that xenbaked is in your path
@@ -672,6 +671,9 @@ def main():
parser = setup_cmdline_parser()
(options, args) = parser.parse_args()
+
+ if len(args):
+ parser.error("No parameter required")
if options.mspersample < 0:
parser.error("option --ms_per_sample: invalid negative value: '%d'" %
options.mspersample)
diff --git a/tools/xenstat/libxenstat/src/xenstat.c b/tools/xenstat/libxenstat/src/xenstat.c
index 4af20372d3..8857bd1c27 100644
--- a/tools/xenstat/libxenstat/src/xenstat.c
+++ b/tools/xenstat/libxenstat/src/xenstat.c
@@ -449,36 +449,36 @@ unsigned int xenstat_domain_ssid(xenstat_domain * domain)
/* Get domain states */
unsigned int xenstat_domain_dying(xenstat_domain * domain)
{
- return (domain->state & DOMFLAGS_DYING) == DOMFLAGS_DYING;
+ return (domain->state & XEN_DOMINF_dying) == XEN_DOMINF_dying;
}
unsigned int xenstat_domain_crashed(xenstat_domain * domain)
{
- return ((domain->state & DOMFLAGS_SHUTDOWN) == DOMFLAGS_SHUTDOWN)
- && (((domain->state >> DOMFLAGS_SHUTDOWNSHIFT)
- & DOMFLAGS_SHUTDOWNMASK) == SHUTDOWN_crash);
+ return ((domain->state & XEN_DOMINF_shutdown) == XEN_DOMINF_shutdown)
+ && (((domain->state >> XEN_DOMINF_shutdownshift)
+ & XEN_DOMINF_shutdownmask) == SHUTDOWN_crash);
}
unsigned int xenstat_domain_shutdown(xenstat_domain * domain)
{
- return ((domain->state & DOMFLAGS_SHUTDOWN) == DOMFLAGS_SHUTDOWN)
- && (((domain->state >> DOMFLAGS_SHUTDOWNSHIFT)
- & DOMFLAGS_SHUTDOWNMASK) != SHUTDOWN_crash);
+ return ((domain->state & XEN_DOMINF_shutdown) == XEN_DOMINF_shutdown)
+ && (((domain->state >> XEN_DOMINF_shutdownshift)
+ & XEN_DOMINF_shutdownmask) != SHUTDOWN_crash);
}
unsigned int xenstat_domain_paused(xenstat_domain * domain)
{
- return (domain->state & DOMFLAGS_PAUSED) == DOMFLAGS_PAUSED;
+ return (domain->state & XEN_DOMINF_paused) == XEN_DOMINF_paused;
}
unsigned int xenstat_domain_blocked(xenstat_domain * domain)
{
- return (domain->state & DOMFLAGS_BLOCKED) == DOMFLAGS_BLOCKED;
+ return (domain->state & XEN_DOMINF_blocked) == XEN_DOMINF_blocked;
}
unsigned int xenstat_domain_running(xenstat_domain * domain)
{
- return (domain->state & DOMFLAGS_RUNNING) == DOMFLAGS_RUNNING;
+ return (domain->state & XEN_DOMINF_running) == XEN_DOMINF_running;
}
/* Get the number of networks for a given domain */
diff --git a/tools/xenstore/Makefile b/tools/xenstore/Makefile
index d4d35c7c8c..a18347d4b1 100644
--- a/tools/xenstore/Makefile
+++ b/tools/xenstore/Makefile
@@ -6,7 +6,7 @@ MAJOR = 3.0
MINOR = 0
PROFILE=#-pg
-BASECFLAGS=-Wall -g -Werror
+BASECFLAGS=-Werror
# Make gcc generate dependencies.
BASECFLAGS += -Wp,-MD,.$(@F).d
PROG_DEP = .*.d
diff --git a/tools/xenstore/xsls.c b/tools/xenstore/xsls.c
index 7667752546..cf5ff6e7e6 100644
--- a/tools/xenstore/xsls.c
+++ b/tools/xenstore/xsls.c
@@ -94,14 +94,8 @@ void usage(int argc, char *argv[])
int main(int argc, char *argv[])
{
struct winsize ws;
- int ret;
- int c;
- int show_perm = 0;
-
- struct xs_handle *xsh = xs_daemon_open();
-
- if (xsh == NULL)
- err(1, "xs_daemon_open");
+ int ret, c, socket = 0, show_perm = 0;
+ struct xs_handle *xsh;
#define PAD 2
@@ -110,12 +104,15 @@ int main(int argc, char *argv[])
if (!ret)
max_width = ws.ws_col - PAD;
- while (0 < (c = getopt(argc, argv, "p"))) {
+ while (0 < (c = getopt(argc, argv, "ps"))) {
switch (c) {
case 'p':
show_perm = 1;
max_width -= 16;
break;
+ case 's':
+ socket = 1;
+ break;
case ':':
case '?':
default:
@@ -124,6 +121,10 @@ int main(int argc, char *argv[])
}
}
+ xsh = socket ? xs_daemon_open() : xs_domain_open();
+ if (xsh == NULL)
+ err(1, socket ? "xs_daemon_open" : "xs_domain_open");
+
print_dir(xsh, (argc - optind) == 1 ? argv[optind] : "/", 0, show_perm);
return 0;
diff --git a/tools/xentrace/formats b/tools/xentrace/formats
index 45be9d231e..3460a5f99f 100644
--- a/tools/xentrace/formats
+++ b/tools/xentrace/formats
@@ -1,3 +1,5 @@
+0x0001f001 CPU%(cpu)d %(tsc)d lost_records 0x%(1)08x
+
0x0002f001 CPU%(cpu)d %(tsc)d sched_add_domain [ domid = 0x%(1)08x, edomid = 0x%(2)08x ]
0x0002f002 CPU%(cpu)d %(tsc)d sched_rem_domain [ domid = 0x%(1)08x, edomid = 0x%(2)08x ]
0x0002f003 CPU%(cpu)d %(tsc)d domain_sleep [ domid = 0x%(1)08x, edomid = 0x%(2)08x ]
diff --git a/tools/xm-test/configure.ac b/tools/xm-test/configure.ac
index 7eeeb76eab..87df5cd6bd 100644
--- a/tools/xm-test/configure.ac
+++ b/tools/xm-test/configure.ac
@@ -1,7 +1,7 @@
# xm-test configure.ac input script
# Basic header information
-AC_INIT([xm-test], [1.0.0])
+AC_INIT([xm-test], [1.1.0])
AM_INIT_AUTOMAKE([1.7 foreign])
MK=''; AC_SUBST(MK)
@@ -11,11 +11,10 @@ AC_PROG_CC
#AC_PROG_INSTALL
AC_CHECK_PROG([LILO], lilo, lilo, "no", [$PATH])
-# Right now, we can assume that the lib/ and ramdisk/ directories
-# are two levels above the tests
+# Right now, we can assume that the lib/ directory
+# is two levels above the tests
TESTLIB=../../lib
-RD_PATH=../../ramdisk
-TENV="PYTHONPATH=$PYTHONPATH:$TESTLIB RD_PATH=$RD_PATH"
+TENV="PYTHONPATH=$PYTHONPATH:$TESTLIB"
AC_ARG_ENABLE(hvm-support,
[[ --enable-hvm-support enable hardware virtual machine assist]],
diff --git a/tools/xm-test/lib/XmTestLib/arch.py b/tools/xm-test/lib/XmTestLib/arch.py
index 5625a53546..331ede5414 100644
--- a/tools/xm-test/lib/XmTestLib/arch.py
+++ b/tools/xm-test/lib/XmTestLib/arch.py
@@ -71,7 +71,6 @@ ia_ParavirtDefaults = {"memory" : 64,
ia_HVMDefaults = {"memory" : 64,
"vcpus" : 1,
"acpi" : 0,
- "apic" : 0,
"disk" : ["file:%s/disk.img,ioemu:%s,w!" %
(getRdPath(), BLOCK_ROOT_DEV)],
"kernel" : "/usr/lib/xen/boot/hvmloader",
diff --git a/tools/xm-test/lib/XmTestReport/arch.py b/tools/xm-test/lib/XmTestReport/arch.py
index e4fd16273d..965e7fb9c6 100644
--- a/tools/xm-test/lib/XmTestReport/arch.py
+++ b/tools/xm-test/lib/XmTestReport/arch.py
@@ -28,6 +28,7 @@ _uname_to_arch_map = {
"i486" : "x86",
"i586" : "x86",
"i686" : "x86",
+ "x86_64": "x86_64",
"ia64" : "ia64",
"ppc" : "powerpc",
"ppc64" : "powerpc",
@@ -37,6 +38,9 @@ _arch = _uname_to_arch_map.get(os.uname()[4], "Unknown")
if _arch == "x86":
cpuValues = {"model_name" : "Unknown",
"flags" : "Unknown"}
+elif _arch == "x86_64":
+ cpuValues = {"model_name" : "Unknown",
+ "flags" : "Unknown"}
elif _arch == "ia64":
cpuValues = {"arch" : "Unknown",
"features" : "Unknown"}
diff --git a/tools/xm-test/ramdisk/Makefile.am b/tools/xm-test/ramdisk/Makefile.am
index dd7f5ec058..14bfc9267c 100644
--- a/tools/xm-test/ramdisk/Makefile.am
+++ b/tools/xm-test/ramdisk/Makefile.am
@@ -4,7 +4,7 @@
# Download a pre-built ramdisk.
# INITRD = <Directory to download ramdisk from>
#
-# make initrd:
+# make initrd.img:
#
# Make a ramdisk from scratch.
# BR_URL = <The URL of the Buildroot source code>
@@ -17,9 +17,9 @@ INITRD ?= http://xm-test.xensource.com/ramdisks
BR_ARCH ?= $(shell uname -m | sed -e s/i.86/i386/ -e 's/ppc\(64\)*/powerpc/')
@MK@ifdef BR_SNAPSHOT
- BR_URL = http://buildroot.uclibc.org/downloads/snapshots/buildroot-snapshot.tar.bz2
+@MK@ BR_URL = http://buildroot.uclibc.org/downloads/snapshots/buildroot-snapshot.tar.bz2
@MK@else
- BR_URL ?= http://xm-test.xensource.com/ramdisks/buildroot-20061023.tar.bz2
+@MK@ BR_URL = http://xm-test.xensource.com/ramdisks/buildroot-20061023.tar.bz2
@MK@endif
BR_TAR = $(notdir $(BR_URL))
diff --git a/tools/xm-test/ramdisk/README-XenSource-initrd-1.0-img b/tools/xm-test/ramdisk/README-XenSource-initrd-1.0-img
index a77476a4a6..80ac26be79 100644
--- a/tools/xm-test/ramdisk/README-XenSource-initrd-1.0-img
+++ b/tools/xm-test/ramdisk/README-XenSource-initrd-1.0-img
@@ -7,7 +7,8 @@ suitable for use with Xen's xm-test regression testing suite. They has been
built and provided by XenSource, for the convenience of Xen users. xm-test
initrds may be mixed across minor xm-test versions, but not across major
versions; this initrd is suitable for all 1.0.x versions of xm-test (as
-shipped with Xen 3.0.4 and the unstable tree leading up to that release).
+shipped with the Xen unstable tree for a short while between Xen 3.0.3 and
+Xen 3.0.4).
In order to use one of these initrds, run "./autogen; ./configure; make
existing" inside the xm-test directory, and the appropriate initrd for your
diff --git a/tools/xm-test/ramdisk/README-XenSource-initrd-1.1-img b/tools/xm-test/ramdisk/README-XenSource-initrd-1.1-img
new file mode 100644
index 0000000000..526c601ccc
--- /dev/null
+++ b/tools/xm-test/ramdisk/README-XenSource-initrd-1.1-img
@@ -0,0 +1,45 @@
+XenSource xm-test 1.1 initrds
+=============================
+
+http://xm-test.xensource.com/ramdisks/initrd-1.1-i386.img and
+http://xm-test.xensource.com/ramdisks/initrd-1.1-powerpc.img are initrds
+suitable for use with Xen's xm-test regression testing suite. They has been
+built and provided by XenSource, for the convenience of Xen users. xm-test
+initrds may be mixed across minor xm-test versions, but not across major
+versions; this initrd is suitable for all 1.1.x versions of xm-test (as
+shipped with Xen 3.0.4 and the unstable tree leading up to that release).
+
+In order to use one of these initrds, run "./autogen; ./configure; make
+existing" inside the xm-test directory, and the appropriate initrd for your
+architecture will be downloaded automatically. Alternatively, if you have
+already downloaded that file, place it into the xm-test/ramdisk directory and
+run the same command. In either case, runtest.sh can then be used as normal.
+See xm-test/README for more details.
+
+These initrds were built using the infrastructure provided by xm-test. Each
+is a full guest operating system and filesystem, and as such includes a large
+number of pieces of software. The source code for the majority of these are
+included in full inside the file
+http://xm-test.xensource.com/ramdisks/<INITRD>-buildroot.tar.bz2, where
+<INITRD> is either initrd-1.1-i386 or initrd-1.1-powerpc as appropriate, or
+alongside this file. Copyright statements and licences are contained therein.
+The remaining source code is included in the Xen distribution, at
+http://www.xensource.com/xen/downloads/archives.html. The configurations used
+for BusyBox, uClibc, and Buildroot are available as
+http://xm-test.xensource.com/ramdisks/<INITRD>-busybox-config,
+http://xm-test.xensource.com/ramdisks/<INITRD>-uClibc-config, and
+http://xm-test.xensource.com/ramdisks/<INITRD>-buildroot-config respectively,
+or alongside this file.
+
+XenSource and the Xen contributors are grateful to the authors of these
+software packages for their contributions to free and open-source software.
+
+
+Buildroot and BusyBox are Copyright (c) Erik Andersen <andersen@codepoet.org>.
+BusyBox is licensed under the GNU General Public License (GPL). A copy of
+this license is available in the file GPL-2,
+http://xm-test.xensource.com/ramdisks/GPL-2, or alongside this file.
+
+uClibc is licensed under the GNU Lesser General Public License (LGPL). A copy
+of this license is available in the file
+http://xm-test.xensource.com/ramdisks/LGPL-2, or alongside this file.
diff --git a/tools/xm-test/ramdisk/make-release.sh b/tools/xm-test/ramdisk/make-release.sh
new file mode 100644
index 0000000000..5aa3831181
--- /dev/null
+++ b/tools/xm-test/ramdisk/make-release.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+if [ "$1" == "" ]
+then
+ arch=""
+elif [ "$1" == "powerpc" ]
+then
+ arch="BR_ARCH=powerpc"
+else
+ echo "Invalid architecture specified." >&2
+ exit 1
+fi
+
+set -eu
+
+tempdir=$(mktemp -d)
+
+dir=$(dirname "$0")
+
+cd $(dirname "$dir")
+
+./autogen
+./configure
+
+cd "$dir"
+
+rm -Rf buildroot
+make $arch initrd.img
+
+initrd=$(readlink "initrd.img")
+prefix=$(basename "$initrd" ".img")
+arch=$(echo "$prefix" | sed -e 's/.*-//')
+
+cp "$initrd" "$tempdir"
+
+cp "buildroot/.config" "$tempdir/$prefix-buildroot-config"
+cp "buildroot/package/busybox/busybox.config" "$tempdir/$prefix-busybox-config"
+cp "buildroot/toolchain/uClibc/uClibc.config" "$tempdir/$prefix-uClibc-config"
+
+mv "buildroot" "$tempdir/buildroot-$arch"
+cd $tempdir
+rm -Rf "buildroot-$arch"/toolchain_build*
+rm -Rf "buildroot-$arch"/build_*
+tar cjf "$prefix-buildroot.tar.bz2" "buildroot-$arch"
+rm -Rf "buildroot-$arch"
+
+echo -e "\n\nYour release is in $tempdir."
diff --git a/tools/xm-test/ramdisk/patches/buildroot/add_xvd_devices.patch b/tools/xm-test/ramdisk/patches/buildroot/add_xvd_devices.patch
new file mode 100644
index 0000000000..25d073da68
--- /dev/null
+++ b/tools/xm-test/ramdisk/patches/buildroot/add_xvd_devices.patch
@@ -0,0 +1,13 @@
+--- buildroot/target/generic/device_table.txt~ 2006-10-26 17:38:04.000000000 +1000
++++ buildroot/target/generic/device_table.txt 2006-10-26 17:37:08.000000000 +1000
+@@ -169,3 +169,10 @@
+ #/dev/mcd b 640 0 0 23 0 0 0 -
+ #/dev/optcd b 640 0 0 17 0 0 0 -
+
++# Xen Virtual Block Devices
++/dev/xvda b 640 0 0 202 0 0 0 -
++/dev/xvda b 640 0 0 202 1 1 1 4
++/dev/xvdb b 640 0 0 202 16 0 0 -
++/dev/xvdb b 640 0 0 202 17 1 1 4
++/dev/xvdc b 640 0 0 202 32 0 0 -
++/dev/xvdc b 640 0 0 202 33 1 1 4
diff --git a/tools/xm-test/runtest.sh b/tools/xm-test/runtest.sh
index 7008972fae..22c0736bf2 100755
--- a/tools/xm-test/runtest.sh
+++ b/tools/xm-test/runtest.sh
@@ -78,7 +78,13 @@ runnable_tests() {
eval $(./lib/XmTestReport/xmtest.py)
ARCH=$(uname -m | sed -e s/i.86/i386/ -e 's/ppc\(64\)*/powerpc/')
rrdver="initrd-${XM_TEST_MAJ}.${XM_TEST_MIN}-${ARCH}.img"
- if [ "$realrd" != "$rrdver" ]; then
+ exp_flag=0
+ realarch=`echo $realrd | awk -F- '{print $3}' | awk -F. '{print $1}'`
+ rrdarch=`echo $rrdver | awk -F- '{print $3}' | awk -F. '{print $1}'`
+ if [ "$realarch" = "i386" -a "$rrdarch" = "x86_64" ]; then
+ exp_flag=1
+ fi
+ if [ $exp_flag -eq 0 -a "$realrd" != "$rrdver" ]; then
echo "Error: ramdisk/initrd.img is from an old version, or is not for this "
echo "architecture ($ARCH)."
echo "You need to build a ramdisk from at least ${XM_TEST_MAJ}.${XM_TEST_MIN}"
@@ -135,7 +141,11 @@ get_contact_info() {
run_tests() {
groupentered=$1
output=$2
+ report=$3
+ startfile=${report}.start
+ stopfile=${report}.stop
+ date -R > $startfile
exec < grouptest/$groupentered
while read casename testlist; do
echo Running $casename tests...
@@ -149,6 +159,7 @@ run_tests() {
fi
done
+ date -R > $stopfile
}
@@ -158,7 +169,10 @@ make_text_reports() {
failures=$2
output=$3
reportfile=$4
+ report=$5
summary=summary.tmp
+ startfile=${report}.start
+ stopfile=${report}.stop
echo "Making PASS/FAIL report ($passfail)..."
cat $OUTPUT | egrep '(REASON|PASS|FAIL|XPASS|XFAIL|SKIP)' | perl -pe 's/^(PASS|FAIL|XPASS|XFAIL)(.+)$/$1$2\n/' > $passfail
@@ -169,7 +183,12 @@ make_text_reports() {
NUMFAIL=`grep -c FAIL $output`
NUMXPASS=`grep -c XPASS $output`
NUMXFAIL=`grep -c XFAIL $output`
+ START=`cat $startfile`
+ STOP=`cat $stopfile`
cat > $summary << EOF
+Xm-test timing summary:
+ Run Started : $START
+ Run Stoped : $STOP
Xm-test execution summary:
PASS: $NUMPASS
FAIL: $NUMFAIL
@@ -296,8 +315,8 @@ if [ "$run" != "no" ]; then
if [ "$unsafe" = "no" ]; then
make_environment_report $OSREPORTTEMP $PROGREPORTTEMP
fi
- run_tests $GROUPENTERED $OUTPUT
- make_text_reports $PASSFAIL $FAILURES $OUTPUT $TXTREPORT
+ run_tests $GROUPENTERED $OUTPUT $REPORT
+ make_text_reports $PASSFAIL $FAILURES $OUTPUT $TXTREPORT $REPORT
if [ "$unsafe" = "no" ]; then
make_result_report $OUTPUT $RESULTREPORTTEMP
cat $OSREPORTTEMP $PROGREPORTTEMP $RESULTREPORTTEMP > $XMLREPORT
diff --git a/tools/xm-test/tests/block-create/01_block_attach_device_pos.py b/tools/xm-test/tests/block-create/01_block_attach_device_pos.py
index 13e6a5efb8..5bbf1232b3 100644
--- a/tools/xm-test/tests/block-create/01_block_attach_device_pos.py
+++ b/tools/xm-test/tests/block-create/01_block_attach_device_pos.py
@@ -32,12 +32,12 @@ except ConsoleError, e:
FAIL(str(e))
-block_attach(domain, "phy:ram1", "sdb1")
+block_attach(domain, "phy:ram1", "xvda1")
-try:
- run = console.runCmd("cat /proc/partitions")
+try:
+ run = console.runCmd("cat /proc/partitions")
except ConsoleError, e:
- FAIL(str(e))
+ FAIL(str(e))
# Close the console
domain.closeConsole()
@@ -45,5 +45,5 @@ domain.closeConsole()
# Stop the domain (nice shutdown)
domain.stop()
-if not re.search("sdb1",run["output"]):
+if not re.search("xvda1",run["output"]):
FAIL("Device is not actually connected to the domU")
diff --git a/tools/xm-test/tests/block-create/02_block_attach_file_device_pos.py b/tools/xm-test/tests/block-create/02_block_attach_file_device_pos.py
index b4e7c6974a..e70f58398b 100644
--- a/tools/xm-test/tests/block-create/02_block_attach_file_device_pos.py
+++ b/tools/xm-test/tests/block-create/02_block_attach_file_device_pos.py
@@ -32,10 +32,10 @@ except ConsoleError, e:
FAIL(str(e))
-block_attach(domain, "file:/dev/ram1", "sdb2")
+block_attach(domain, "file:/dev/ram1", "xvda1")
try:
- run = console.runCmd("cat /proc/partitions")
+ run = console.runCmd("cat /proc/partitions")
except ConsoleError, e:
FAIL(str(e))
@@ -45,5 +45,5 @@ domain.closeConsole()
# Stop the domain (nice shutdown)
domain.stop()
-if not re.search("sdb2",run["output"]):
- FAIL("Device is not actually connected to the domU")
+if not re.search("xvda1",run["output"]):
+ FAIL("Device is not actually connected to the domU")
diff --git a/tools/xm-test/tests/block-create/04_block_attach_device_repeatedly_pos.py b/tools/xm-test/tests/block-create/04_block_attach_device_repeatedly_pos.py
index 6d9eb513fe..2e258840c4 100644
--- a/tools/xm-test/tests/block-create/04_block_attach_device_repeatedly_pos.py
+++ b/tools/xm-test/tests/block-create/04_block_attach_device_repeatedly_pos.py
@@ -30,14 +30,14 @@ except ConsoleError, e:
FAIL(str(e))
for i in range(10):
- status, output = traceCommand("xm block-attach %s phy:ram1 sdb1 w" % domain.getName())
- if i == 0 and status != 0:
- FAIL("xm block attach returned invalid %i != 0" % status)
- if i > 0 and status == 0:
- FAIL("xm block-attach (repeat) returned invalid %i > 0" % status)
- run = console.runCmd("cat /proc/partitions")
- if not re.search("sdb1", run['output']):
- FAIL("Device is not actually attached to domU")
+ status, output = traceCommand("xm block-attach %s phy:ram1 xvda1 w" % domain.getName())
+ if i == 0 and status != 0:
+ FAIL("xm block attach returned invalid %i != 0" % status)
+ if i > 0 and status == 0:
+ FAIL("xm block-attach (repeat) returned invalid %i > 0" % status)
+ run = console.runCmd("cat /proc/partitions")
+ if not re.search("xvda1", run['output']):
+ FAIL("Device is not actually attached to domU")
# Close the console
domain.closeConsole()
diff --git a/tools/xm-test/tests/block-create/05_block_attach_and_dettach_device_repeatedly_pos.py b/tools/xm-test/tests/block-create/05_block_attach_and_dettach_device_repeatedly_pos.py
index db406fecd4..325e160937 100644
--- a/tools/xm-test/tests/block-create/05_block_attach_and_dettach_device_repeatedly_pos.py
+++ b/tools/xm-test/tests/block-create/05_block_attach_and_dettach_device_repeatedly_pos.py
@@ -32,15 +32,15 @@ except ConsoleError, e:
for i in range(10):
- block_attach(domain, "phy:ram1", "sdb1")
- run = console.runCmd("cat /proc/partitions")
- if not re.search("sdb1", run["output"]):
- FAIL("Failed to attach block device: /proc/partitions does not show that!")
-
- block_detach(domain, "sdb1")
- run = console.runCmd("cat /proc/partitions")
- if re.search("sdb1", run["output"]):
- FAIL("Failed to dettach block device: /proc/partitions still showing that!")
+ block_attach(domain, "phy:ram1", "xvda1")
+ run = console.runCmd("cat /proc/partitions")
+ if not re.search("xvda1", run["output"]):
+ FAIL("Failed to attach block device: /proc/partitions does not show that!")
+
+ block_detach(domain, "xvda1")
+ run = console.runCmd("cat /proc/partitions")
+ if re.search("xvda1", run["output"]):
+ FAIL("Failed to dettach block device: /proc/partitions still showing that!")
# Close the console
domain.closeConsole()
diff --git a/tools/xm-test/tests/block-create/06_block_attach_baddomain_neg.py b/tools/xm-test/tests/block-create/06_block_attach_baddomain_neg.py
index edfeba1643..958b13e1ea 100644
--- a/tools/xm-test/tests/block-create/06_block_attach_baddomain_neg.py
+++ b/tools/xm-test/tests/block-create/06_block_attach_baddomain_neg.py
@@ -8,13 +8,11 @@ from XmTestLib import *
if ENABLE_HVM_SUPPORT:
SKIP("Block-attach not supported for HVM domains")
-status, output = traceCommand("xm block-attach NOT-EXIST phy:ram1 sdb1 w")
+status, output = traceCommand("xm block-attach NOT-EXIST phy:ram1 xvda1 w")
eyecatcher = "Error"
where = output.find(eyecatcher)
if status == 0:
- FAIL("xm block-attach returned bad status, expected non 0, status is: %i" % status )
+ FAIL("xm block-attach returned bad status, expected non 0, status is: %i" % status )
elif where == -1:
- FAIL("xm block-attach returned bad output, expected Error, output is: %s" % output )
-
-
+ FAIL("xm block-attach returned bad output, expected Error, output is: %s" % output )
diff --git a/tools/xm-test/tests/block-create/07_block_attach_baddevice_neg.py b/tools/xm-test/tests/block-create/07_block_attach_baddevice_neg.py
index f2043d7dd3..3e9f0f2514 100644
--- a/tools/xm-test/tests/block-create/07_block_attach_baddevice_neg.py
+++ b/tools/xm-test/tests/block-create/07_block_attach_baddevice_neg.py
@@ -30,18 +30,18 @@ except ConsoleError, e:
FAIL(str(e))
-status, output = traceCommand("xm block-attach %s phy:NOT-EXIST sdb1 w" % domain.getName())
+status, output = traceCommand("xm block-attach %s phy:NOT-EXIST xvda1 w" % domain.getName())
eyecatcher = "Error"
where = output.find(eyecatcher)
if status == 0:
- FAIL("xm block-attach returned bad status, expected non 0, status is: %i" % status )
+ FAIL("xm block-attach returned bad status, expected non 0, status is: %i" % status )
elif where == -1:
- FAIL("xm block-attach returned bad output, expected Error, output is: %s" % output )
+ FAIL("xm block-attach returned bad output, expected Error, output is: %s" % output )
try:
- run = console.runCmd("cat /proc/partitions")
+ run = console.runCmd("cat /proc/partitions")
except ConsoleError, e:
- FAIL(str(e))
+ FAIL(str(e))
# Close the console
domain.closeConsole()
@@ -49,5 +49,5 @@ domain.closeConsole()
# Stop the domain (nice shutdown)
domain.stop()
-if re.search("sdb1",run["output"]):
- FAIL("Non existent Device was connected to the domU")
+if re.search("xvda1",run["output"]):
+ FAIL("Non existent Device was connected to the domU")
diff --git a/tools/xm-test/tests/block-create/08_block_attach_bad_filedevice_neg.py b/tools/xm-test/tests/block-create/08_block_attach_bad_filedevice_neg.py
index b1c776d71a..802e101147 100644
--- a/tools/xm-test/tests/block-create/08_block_attach_bad_filedevice_neg.py
+++ b/tools/xm-test/tests/block-create/08_block_attach_bad_filedevice_neg.py
@@ -29,18 +29,18 @@ except ConsoleError, e:
saveLog(console.getHistory())
FAIL(str(e))
-status, output = traceCommand("xm block-attach %s file:/dev/NOT-EXIST sdb1 w" % domain.getName())
+status, output = traceCommand("xm block-attach %s file:/dev/NOT-EXIST xvda1 w" % domain.getName())
eyecatcher = "Error"
where = output.find(eyecatcher)
if status == 0:
- FAIL("xm block-attach returned bad status, expected non 0, status is: %i" % status )
+ FAIL("xm block-attach returned bad status, expected non 0, status is: %i" % status )
elif where == -1:
- FAIL("xm block-attach returned bad output, expected Error, output is: %s" % output )
-
+ FAIL("xm block-attach returned bad output, expected Error, output is: %s" % output )
+
try:
- run = console.runCmd("cat /proc/partitions")
+ run = console.runCmd("cat /proc/partitions")
except ConsoleError, e:
- FAIL(str(e))
+ FAIL(str(e))
# Close the console
domain.closeConsole()
@@ -48,5 +48,5 @@ domain.closeConsole()
# Stop the domain (nice shutdown)
domain.stop()
-if re.search("sdb1",run["output"]):
- FAIL("Non existent Device was connected to the domU")
+if re.search("xvda1",run["output"]):
+ FAIL("Non existent Device was connected to the domU")
diff --git a/tools/xm-test/tests/block-create/09_block_attach_and_dettach_device_check_data_pos.py b/tools/xm-test/tests/block-create/09_block_attach_and_dettach_device_check_data_pos.py
index b997379a25..b97b70c499 100644
--- a/tools/xm-test/tests/block-create/09_block_attach_and_dettach_device_check_data_pos.py
+++ b/tools/xm-test/tests/block-create/09_block_attach_and_dettach_device_check_data_pos.py
@@ -12,7 +12,7 @@ if ENABLE_HVM_SUPPORT:
SKIP("Block-attach not supported for HVM domains")
# Create a domain (default XmTestDomain, with our ramdisk)
-domain = XmTestDomain()
+domain = XmTestDomain(extraConfig={"extra":"rw"})
try:
console = domain.start()
@@ -35,27 +35,27 @@ if s != 0:
FAIL("mke2fs returned %i != 0" % s)
for i in range(10):
- block_attach(domain, "phy:ram1", "hda1")
- run = console.runCmd("cat /proc/partitions")
- if not re.search("hda1", run["output"]):
- FAIL("Failed to attach block device: /proc/partitions does not show that!")
-
- console.runCmd("mkdir -p /mnt/hda1; mount /dev/hda1 /mnt/hda1")
-
- if i:
- run = console.runCmd("cat /mnt/hda1/myfile | grep %s" % (i-1))
- if run['return']:
- FAIL("File created was lost or not updated!")
-
- console.runCmd("echo \"%s\" > /mnt/hda1/myfile" % i)
- run = console.runCmd("cat /mnt/hda1/myfile")
- print run['output']
- console.runCmd("umount /mnt/hda1")
-
- block_detach(domain, "hda1")
- run = console.runCmd("cat /proc/partitions")
- if re.search("hda1", run["output"]):
- FAIL("Failed to dettach block device: /proc/partitions still showing that!")
+ block_attach(domain, "phy:ram1", "xvda1")
+ run = console.runCmd("cat /proc/partitions")
+ if not re.search("xvda1", run["output"]):
+ FAIL("Failed to attach block device: /proc/partitions does not show that!")
+
+ console.runCmd("mkdir -p /mnt/xvda1; mount /dev/xvda1 /mnt/xvda1")
+
+ if i:
+ run = console.runCmd("cat /mnt/xvda1/myfile | grep %s" % (i-1))
+ if run['return']:
+ FAIL("File created was lost or not updated!")
+
+ console.runCmd("echo \"%s\" > /mnt/xvda1/myfile" % i)
+ run = console.runCmd("cat /mnt/xvda1/myfile")
+ print run['output']
+ console.runCmd("umount /mnt/xvda1")
+
+ block_detach(domain, "xvda1")
+ run = console.runCmd("cat /proc/partitions")
+ if re.search("xvda1", run["output"]):
+ FAIL("Failed to dettach block device: /proc/partitions still showing that!")
# Close the console
domain.closeConsole()
diff --git a/tools/xm-test/tests/block-create/10_block_attach_dettach_multiple_devices.py b/tools/xm-test/tests/block-create/10_block_attach_dettach_multiple_devices.py
index 833f75c21a..3ac6078388 100644
--- a/tools/xm-test/tests/block-create/10_block_attach_dettach_multiple_devices.py
+++ b/tools/xm-test/tests/block-create/10_block_attach_dettach_multiple_devices.py
@@ -15,7 +15,7 @@ from XmTestLib.block_utils import *
def availableRamdisks():
i = 0
while os.access("/dev/ram%d" % i, os.F_OK ):
- i += 1
+ i += 1
return i
@@ -36,7 +36,7 @@ def detach(devname):
return -2, "Failed to detach block device: /proc/partitions still showing that!"
return 0, None
-
+
if ENABLE_HVM_SUPPORT:
SKIP("Block-attach not supported for HVM domains")
@@ -69,22 +69,22 @@ while i < ramdisks or devices:
op = random.randint(0,1) # 1 = attach, 0 = detach
if (not devices or op) and i < ramdisks:
i += 1
- devname = "/dev/hda%d" % i
- phy = "/dev/ram%d" % i
- print "Attaching %s to %s" % (devname, phy)
- status, msg = attach( phy, devname )
- if status:
- FAIL(msg)
- else:
- devices.append(devname)
+ devname = "/dev/xvda%d" % i
+ phy = "/dev/ram%d" % i
+ print "Attaching %s to %s" % (devname, phy)
+ status, msg = attach( phy, devname )
+ if status:
+ FAIL(msg)
+ else:
+ devices.append(devname)
elif devices:
devname = random.choice(devices)
- devices.remove(devname)
- print "Detaching %s" % devname
- status, msg = detach(devname)
- if status:
- FAIL(msg)
+ devices.remove(devname)
+ print "Detaching %s" % devname
+ status, msg = detach(devname)
+ if status:
+ FAIL(msg)
# Close the console
domain.closeConsole()
diff --git a/tools/xm-test/tests/block-create/11_block_attach_shared_dom0.py b/tools/xm-test/tests/block-create/11_block_attach_shared_dom0.py
index 43f55e234c..1b8a289394 100644
--- a/tools/xm-test/tests/block-create/11_block_attach_shared_dom0.py
+++ b/tools/xm-test/tests/block-create/11_block_attach_shared_dom0.py
@@ -24,7 +24,7 @@ if s != 0:
# Now try to start a DomU with write access to /dev/ram0
-config = {"disk":"phy:/dev/ram0,hda1,w"}
+config = {"disk":"phy:/dev/ram0,xvda1,w"}
domain = XmTestDomain(extraConfig=config);
diff --git a/tools/xm-test/tests/block-create/12_block_attach_shared_domU.py b/tools/xm-test/tests/block-create/12_block_attach_shared_domU.py
index 362e5039e8..79c9571f8c 100644
--- a/tools/xm-test/tests/block-create/12_block_attach_shared_domU.py
+++ b/tools/xm-test/tests/block-create/12_block_attach_shared_domU.py
@@ -8,7 +8,7 @@ from XmTestLib import *
if ENABLE_HVM_SUPPORT:
SKIP("Block-attach not supported for HVM domains")
-config = {"disk":"phy:/dev/ram0,hda1,w"}
+config = {"disk":"phy:/dev/ram0,xvda1,w"}
dom1 = XmTestDomain(extraConfig=config)
dom2 = XmTestDomain(dom1.getName() + "-2",
diff --git a/tools/xm-test/tests/block-destroy/01_block-destroy_btblock_pos.py b/tools/xm-test/tests/block-destroy/01_block-destroy_btblock_pos.py
index 835dfa9b1d..1d056841cc 100644
--- a/tools/xm-test/tests/block-destroy/01_block-destroy_btblock_pos.py
+++ b/tools/xm-test/tests/block-destroy/01_block-destroy_btblock_pos.py
@@ -9,7 +9,7 @@ from XmTestLib.block_utils import block_detach
if ENABLE_HVM_SUPPORT:
SKIP("Block-detach not supported for HVM domains")
-config = {"disk":"phy:/dev/ram0,hda1,w"}
+config = {"disk":"phy:/dev/ram0,xvda1,w"}
domain = XmTestDomain(extraConfig=config)
try:
@@ -21,7 +21,7 @@ except DomainError, e:
try:
console.setHistorySaveCmds(value=True)
- run = console.runCmd("cat /proc/partitions | grep hda1")
+ run = console.runCmd("cat /proc/partitions | grep xvda1")
run2 = console.runCmd("cat /proc/partitions")
except ConsoleError, e:
FAIL(str(e))
@@ -29,10 +29,10 @@ except ConsoleError, e:
if run["return"] != 0:
FAIL("block device isn't attached; can't detach!")
-block_detach(domain, "hda1")
+block_detach(domain, "xvda1")
try:
- run = console.runCmd("cat /proc/partitions | grep hda1")
+ run = console.runCmd("cat /proc/partitions | grep xvda1")
except ConsoleError, e:
saveLog(console.getHistory())
FAIL(str(e))
diff --git a/tools/xm-test/tests/block-destroy/02_block-destroy_rtblock_pos.py b/tools/xm-test/tests/block-destroy/02_block-destroy_rtblock_pos.py
index 47ff9a6fe5..cf6329228a 100644
--- a/tools/xm-test/tests/block-destroy/02_block-destroy_rtblock_pos.py
+++ b/tools/xm-test/tests/block-destroy/02_block-destroy_rtblock_pos.py
@@ -18,9 +18,9 @@ except DomainError, e:
print e.extra
FAIL("Unable to create domain")
-block_attach(domain, "phy:/dev/ram0", "hda1")
+block_attach(domain, "phy:/dev/ram0", "xvda1")
try:
- run = console.runCmd("cat /proc/partitions | grep hda1")
+ run = console.runCmd("cat /proc/partitions | grep xvda1")
except ConsoleError, e:
saveLog(console.getHistory())
FAIL(str(e))
@@ -28,9 +28,9 @@ except ConsoleError, e:
if run["return"] != 0:
FAIL("Failed to verify that block dev is attached")
-block_detach(domain, "hda1")
+block_detach(domain, "xvda1")
try:
- run = console.runCmd("cat /proc/partitions | grep hda1")
+ run = console.runCmd("cat /proc/partitions | grep xvda1")
except ConsoleError, e:
saveLog(console.getHistory())
FAIL(str(e))
diff --git a/tools/xm-test/tests/block-destroy/04_block-destroy_nonattached_neg.py b/tools/xm-test/tests/block-destroy/04_block-destroy_nonattached_neg.py
index eea2027771..d7df7ac317 100644
--- a/tools/xm-test/tests/block-destroy/04_block-destroy_nonattached_neg.py
+++ b/tools/xm-test/tests/block-destroy/04_block-destroy_nonattached_neg.py
@@ -19,7 +19,7 @@ except DomainError, e:
print e.extra
FAIL("Unable to create domain")
-status, output = traceCommand("xm block-detach %s sda1" % domain.getId())
+status, output = traceCommand("xm block-detach %s xvda1" % domain.getId())
eyecatcher1 = "Error:"
eyecatcher2 = "Traceback"
diff --git a/tools/xm-test/tests/block-destroy/05_block-destroy_byname_pos.py b/tools/xm-test/tests/block-destroy/05_block-destroy_byname_pos.py
index d77e587c33..7e3d9904da 100644
--- a/tools/xm-test/tests/block-destroy/05_block-destroy_byname_pos.py
+++ b/tools/xm-test/tests/block-destroy/05_block-destroy_byname_pos.py
@@ -9,7 +9,7 @@ from XmTestLib.block_utils import block_detach
if ENABLE_HVM_SUPPORT:
SKIP("Block-detach not supported for HVM domains")
-config = {"disk":"phy:/dev/ram0,hda1,w"}
+config = {"disk":"phy:/dev/ram0,xvda1,w"}
domain = XmTestDomain(extraConfig=config)
try:
@@ -20,7 +20,7 @@ except DomainError, e:
FAIL("Unable to create domain")
try:
- run = console.runCmd("cat /proc/partitions | grep hda1")
+ run = console.runCmd("cat /proc/partitions | grep xvda1")
run2 = console.runCmd("cat /proc/partitions")
except ConsoleError, e:
FAIL(str(e))
@@ -28,10 +28,10 @@ except ConsoleError, e:
if run["return"] != 0:
FAIL("block device isn't attached; can't detach!")
-block_detach(domain, "hda1")
+block_detach(domain, "xvda1")
try:
- run = console.runCmd("cat /proc/partitions | grep hda1")
+ run = console.runCmd("cat /proc/partitions | grep xvda1")
except ConsoleError, e:
saveLog(console.getHistory())
FAIL(str(e))
diff --git a/tools/xm-test/tests/block-destroy/06_block-destroy_check_list_pos.py b/tools/xm-test/tests/block-destroy/06_block-destroy_check_list_pos.py
index 38d60a92c1..292db063d6 100644
--- a/tools/xm-test/tests/block-destroy/06_block-destroy_check_list_pos.py
+++ b/tools/xm-test/tests/block-destroy/06_block-destroy_check_list_pos.py
@@ -12,7 +12,7 @@ def checkXmLongList(domain):
s, o = traceCommand("xm list --long %s" % domain.getName())
if s != 0:
FAIL("xm list --long <dom> failed")
- if re.search("hda1", o):
+ if re.search("xvda1", o):
return True
else:
return False
@@ -27,12 +27,12 @@ try:
except DomainError,e:
FAIL(str(e))
-block_attach(domain, "phy:/dev/ram0", "hda1")
+block_attach(domain, "phy:/dev/ram0", "xvda1")
if not checkXmLongList(domain):
- FAIL("xm long list does not show that hda1 was attached")
+ FAIL("xm long list does not show that xvda1 was attached")
-block_detach(domain, "hda1")
+block_detach(domain, "xvda1")
if checkXmLongList(domain):
- FAIL("xm long list does not show that hda1 was removed")
+ FAIL("xm long list does not show that xvda1 was removed")
diff --git a/tools/xm-test/tests/block-integrity/01_block_device_read_verify.py b/tools/xm-test/tests/block-integrity/01_block_device_read_verify.py
index b4f03da628..28531f1559 100644
--- a/tools/xm-test/tests/block-integrity/01_block_device_read_verify.py
+++ b/tools/xm-test/tests/block-integrity/01_block_device_read_verify.py
@@ -33,10 +33,10 @@ s, o = traceCommand("md5sum /dev/ram1")
dom0_md5sum_match = re.search(r"^[\dA-Fa-f]{32}", o, re.M)
-block_attach(domain, "phy:ram1", "hda1")
+block_attach(domain, "phy:ram1", "xvda1")
try:
- run = console.runCmd("md5sum /dev/hda1")
+ run = console.runCmd("md5sum /dev/xvda1")
except ConsoleError, e:
FAIL(str(e))
diff --git a/tools/xm-test/tests/block-integrity/02_block_device_write_verify.py b/tools/xm-test/tests/block-integrity/02_block_device_write_verify.py
index f8fa19aa8b..ed791a008a 100644
--- a/tools/xm-test/tests/block-integrity/02_block_device_write_verify.py
+++ b/tools/xm-test/tests/block-integrity/02_block_device_write_verify.py
@@ -28,12 +28,12 @@ except DomainError, e:
console.setHistorySaveCmds(value=True)
-block_attach(domain, "phy:ram1", "hda1")
+block_attach(domain, "phy:ram1", "xvda1")
console.setTimeout(120)
try:
- run = console.runCmd("dd if=/dev/urandom bs=512 count=`cat /sys/block/hda1/size` | tee /dev/hda1 | md5sum")
+ run = console.runCmd("dd if=/dev/urandom bs=512 count=`cat /sys/block/xvda1/size` | tee /dev/xvda1 | md5sum")
except ConsoleError, e:
FAIL(str(e))
diff --git a/tools/xm-test/tests/block-list/01_block-list_pos.py b/tools/xm-test/tests/block-list/01_block-list_pos.py
index 05df76c189..2b19208d97 100644
--- a/tools/xm-test/tests/block-list/01_block-list_pos.py
+++ b/tools/xm-test/tests/block-list/01_block-list_pos.py
@@ -11,7 +11,7 @@ from XmTestLib import *
if ENABLE_HVM_SUPPORT:
SKIP("Block-list not supported for HVM domains")
-config = {"disk":"phy:/dev/ram0,hda1,w"}
+config = {"disk":"phy:/dev/ram0,xvda1,w"}
domain = XmTestDomain(extraConfig=config)
try:
@@ -22,7 +22,7 @@ except DomainError, e:
FAIL("Unable to create domain")
status, output = traceCommand("xm block-list %s" % domain.getId())
-eyecatcher = "769"
+eyecatcher = "51713"
where = output.find(eyecatcher)
if status != 0:
FAIL("xm block-list returned bad status, expected 0, status is %i" % status)
@@ -31,7 +31,7 @@ elif where < 0:
#Verify the block device on DomainU
try:
- run = console.runCmd("cat /proc/partitions | grep hda1")
+ run = console.runCmd("cat /proc/partitions | grep xvda1")
except ConsoleError, e:
saveLog(console.getHistory())
FAIL(str(e))
diff --git a/tools/xm-test/tests/block-list/02_block-list_attachbd_pos.py b/tools/xm-test/tests/block-list/02_block-list_attachbd_pos.py
index 256b44d951..8ad0df3a0c 100644
--- a/tools/xm-test/tests/block-list/02_block-list_attachbd_pos.py
+++ b/tools/xm-test/tests/block-list/02_block-list_attachbd_pos.py
@@ -22,11 +22,11 @@ except DomainError, e:
FAIL("Unable to create domain")
#Attach one virtual block device to domainU
-block_attach(domain, "phy:/dev/ram0", "hda1")
+block_attach(domain, "phy:/dev/ram0", "xvda1")
#Verify block-list on Domain0
status, output = traceCommand("xm block-list %s" % domain.getId())
-eyecatcher = "769"
+eyecatcher = "51713"
where = output.find(eyecatcher)
if status != 0:
FAIL("xm block-list returned bad status, expected 0, status is %i" % status)
@@ -35,7 +35,7 @@ elif where < 0 :
#Verify attached block device on DomainU
try:
- run = console.runCmd("cat /proc/partitions | grep hda1")
+ run = console.runCmd("cat /proc/partitions | grep xvda1")
except ConsoleError, e:
saveLog(console.getHistory())
FAIL(str(e))
diff --git a/tools/xm-test/tests/block-list/03_block-list_anotherbd_pos.py b/tools/xm-test/tests/block-list/03_block-list_anotherbd_pos.py
index 1556b065ef..b128ec1bba 100644
--- a/tools/xm-test/tests/block-list/03_block-list_anotherbd_pos.py
+++ b/tools/xm-test/tests/block-list/03_block-list_anotherbd_pos.py
@@ -11,7 +11,7 @@ from XmTestLib import *
if ENABLE_HVM_SUPPORT:
SKIP("Block-list not supported for HVM domains")
-config = {"disk":"phy:/dev/ram0,hda1,w"}
+config = {"disk":"phy:/dev/ram0,xvda1,w"}
domain = XmTestDomain(extraConfig=config)
try:
@@ -26,14 +26,14 @@ if status != 0:
FAIL("Fail to list block device")
#Add another virtual block device to the domain
-status, output = traceCommand("xm block-attach %s phy:/dev/ram1 hda2 w" % domain.getId())
+status, output = traceCommand("xm block-attach %s phy:/dev/ram1 xvda2 w" % domain.getId())
if status != 0:
FAIL("Fail to attach block device")
#Verify block-list on Domain0
status, output = traceCommand("xm block-list %s" % domain.getId())
-eyecatcher1 = "769"
-eyecatcher2 = "770"
+eyecatcher1 = "51713"
+eyecatcher2 = "51714"
where1 = output.find(eyecatcher1)
where2 = output.find(eyecatcher2)
if status != 0:
@@ -43,7 +43,7 @@ elif (where1 < 0) and (where2 < 0):
#Verify attached block device on DomainU
try:
- run = console.runCmd("cat /proc/partitions | grep hda1;cat /proc/partitions | grep hda2")
+ run = console.runCmd("cat /proc/partitions | grep xvda1;cat /proc/partitions | grep xvda2")
except ConsoleError, e:
saveLog(console.getHistory())
FAIL(str(e))
diff --git a/tools/xm-test/tests/block-list/06_block-list_checkremove_pos.py b/tools/xm-test/tests/block-list/06_block-list_checkremove_pos.py
index db2c54104d..8d3bf08c42 100644
--- a/tools/xm-test/tests/block-list/06_block-list_checkremove_pos.py
+++ b/tools/xm-test/tests/block-list/06_block-list_checkremove_pos.py
@@ -22,39 +22,39 @@ if s != 0:
if o:
FAIL("block-list without devices reported something!")
-block_attach(domain, "phy:/dev/ram0", "hda1")
+block_attach(domain, "phy:/dev/ram0", "xvda1")
s, o = traceCommand("xm block-list %s" % domain.getName())
if s != 0:
FAIL("block-list failed")
-if o.find("769") == -1:
+if o.find("51713") == -1:
FAIL("block-list didn't show the block device I just attached!")
-block_attach(domain, "phy:/dev/ram1", "hda2")
+block_attach(domain, "phy:/dev/ram1", "xvda2")
s, o = traceCommand("xm block-list %s" % domain.getName())
if s != 0:
FAIL("block-list failed")
-if o.find("770") == -1:
+if o.find("51714") == -1:
FAIL("block-list didn't show the other block device I just attached!")
-block_detach(domain, "hda1")
+block_detach(domain, "xvda1")
s, o = traceCommand("xm block-list %s" % domain.getName())
if s != 0:
FAIL("block-list failed after detaching a device")
-if o.find("769") != -1:
- FAIL("hda1 still shown in block-list after detach!")
-if o.find("770") == -1:
- FAIL("hda2 not shown after detach of hda1!")
+if o.find("51713") != -1:
+ FAIL("xvda1 still shown in block-list after detach!")
+if o.find("51714") == -1:
+ FAIL("xvda2 not shown after detach of xvda1!")
-block_detach(domain, "hda2")
+block_detach(domain, "xvda2")
s, o = traceCommand("xm block-list %s" % domain.getName())
if s != 0:
FAIL("block-list failed after detaching another device")
-if o.find("770") != -1:
- FAIL("hda2 still shown in block-list after detach!")
+if o.find("51714") != -1:
+ FAIL("xvda2 still shown in block-list after detach!")
if o:
FAIL("block-list still shows something after all devices detached!")
diff --git a/tools/xm-test/tests/create/07_create_mem64_pos.py b/tools/xm-test/tests/create/07_create_mem64_pos.py
index ae2f84adb1..1a27d55722 100644
--- a/tools/xm-test/tests/create/07_create_mem64_pos.py
+++ b/tools/xm-test/tests/create/07_create_mem64_pos.py
@@ -42,7 +42,7 @@ if eyecatcher1 != "True":
FAIL("Failed to verify that a 64MB domain started")
eyecatcher2 = getDomMem(domain_mem64.getName())
-if eyecatcher2 != 64:
+if eyecatcher2 not in range(62, 65):
FAIL("Started domain with 64MB, but it got %i MB" % eyecatcher2)
#stop the domain (nice shutdown)
diff --git a/tools/xm-test/tests/create/08_create_mem128_pos.py b/tools/xm-test/tests/create/08_create_mem128_pos.py
index 0d50006b36..85d35173c7 100644
--- a/tools/xm-test/tests/create/08_create_mem128_pos.py
+++ b/tools/xm-test/tests/create/08_create_mem128_pos.py
@@ -42,7 +42,7 @@ if eyecatcher1 != "True":
FAIL("Failed to verify that a 128MB domain started")
eyecatcher2 = getDomMem(domain_mem128.getName())
-if eyecatcher2 != 128:
+if eyecatcher2 not in range(126, 129):
FAIL("Started domain with 128MB, but it got %i MB" % eyecatcher2)
#stop the domain (nice shutdown)
diff --git a/tools/xm-test/tests/create/09_create_mem256_pos.py b/tools/xm-test/tests/create/09_create_mem256_pos.py
index c926d62de8..28db3dade2 100644
--- a/tools/xm-test/tests/create/09_create_mem256_pos.py
+++ b/tools/xm-test/tests/create/09_create_mem256_pos.py
@@ -42,7 +42,7 @@ if eyecatcher1 != "True":
FAIL("Failed to verify that a 256MB domain started")
eyecatcher2 = getDomMem(domain_mem256.getName())
-if eyecatcher2 != 256:
+if eyecatcher2 not in range(254, 257):
FAIL("Started domain with 256MB, but it got %i MB" % eyecatcher2)
#stop the domain (nice shutdown)
diff --git a/tools/xm-test/tests/create/14_create_blockroot_pos.py b/tools/xm-test/tests/create/14_create_blockroot_pos.py
index 58eeb2b9b3..79dd622b5a 100644
--- a/tools/xm-test/tests/create/14_create_blockroot_pos.py
+++ b/tools/xm-test/tests/create/14_create_blockroot_pos.py
@@ -18,17 +18,12 @@ rdpath = getRdPath()
# print "Using %s" % output
if ENABLE_HVM_SUPPORT:
- domain = XmTestDomain(name="14_create_blockroot")
+ config = None
else:
- config = {"memory" : "64",
- "root" : "/dev/hda1",
- "name" : "14_create_blockroot",
- "kernel" : getDefaultKernel(),
+ config = {"root" : "/dev/hda1",
"disk" : "file:%s/initrd.img,hda1,w" % rdpath
}
- domConfig = XenConfig()
- domConfig.setOpts(config)
- domain = XenDomain(name=domConfig.getOpt("name"), config=domConfig)
+domain = XmTestDomain(name="14_create_blockroot", extraConfig=config)
try:
console = domain.start()
diff --git a/tools/xm-test/tests/network-attach/04_network_attach_baddomain_neg.py b/tools/xm-test/tests/network-attach/04_network_attach_baddomain_neg.py
index 838d66c32a..f69adb6e82 100644
--- a/tools/xm-test/tests/network-attach/04_network_attach_baddomain_neg.py
+++ b/tools/xm-test/tests/network-attach/04_network_attach_baddomain_neg.py
@@ -10,8 +10,6 @@ status, output = traceCommand("xm network-attach NOT-EXIST")
eyecatcher = "Error"
where = output.find(eyecatcher)
if status == 0:
- FAIL("xm block-attach returned bad status, expected non 0, status is: %i" % status )
+ FAIL("xm network-attach returned bad status, expected non 0, status is: %i" % status )
elif where == -1:
- FAIL("xm block-attach returned bad output, expected Error, output is: %s" % output )
-
-
+ FAIL("xm network-attach returned bad output, expected Error, output is: %s" % output )
diff --git a/unmodified_drivers/linux-2.6/Makefile b/unmodified_drivers/linux-2.6/Makefile
index 95d558f77b..119016f531 100644
--- a/unmodified_drivers/linux-2.6/Makefile
+++ b/unmodified_drivers/linux-2.6/Makefile
@@ -4,3 +4,4 @@ obj-m += platform-pci/
obj-m += xenbus/
obj-m += blkfront/
obj-m += netfront/
+obj-m += util/
diff --git a/unmodified_drivers/linux-2.6/compat-include/asm-generic/pgtable-nopud.h b/unmodified_drivers/linux-2.6/compat-include/asm-generic/pgtable-nopud.h
index 8b23299dd0..05c9675760 100644
--- a/unmodified_drivers/linux-2.6/compat-include/asm-generic/pgtable-nopud.h
+++ b/unmodified_drivers/linux-2.6/compat-include/asm-generic/pgtable-nopud.h
@@ -9,6 +9,7 @@
#define pud_offset(d, va) d
#define pud_none(pud) 0
#define pud_present(pud) 1
+#define pud_bad(pud) 0
#define PTRS_PER_PUD 1
#endif /* _PGTABLE_NOPUD_H */
diff --git a/unmodified_drivers/linux-2.6/compat-include/xen/platform-compat.h b/unmodified_drivers/linux-2.6/compat-include/xen/platform-compat.h
index 4978c63610..ea0e6b308f 100644
--- a/unmodified_drivers/linux-2.6/compat-include/xen/platform-compat.h
+++ b/unmodified_drivers/linux-2.6/compat-include/xen/platform-compat.h
@@ -25,6 +25,21 @@
#define NET_IP_ALIGN 0
#endif
+#if defined(_LINUX_ERR_H) && !defined(IS_ERR_VALUE)
+#define IS_ERR_VALUE(x) unlikely((x) > (unsigned long)-1000L)
+#endif
+
+#if defined(_ASM_IA64_PGTABLE_H) && !defined(_PGTABLE_NOPUD_H)
+#include <asm-generic/pgtable-nopud.h>
+#endif
+
+/* Some kernels have this typedef backported so we cannot reliably
+ * detect based on version number, hence we forcibly #define it.
+ */
+#if defined(__LINUX_TYPES_H) || defined(__LINUX_GFP_H)
+#define gfp_t unsigned
+#endif
+
#if defined(_LINUX_FS_H) && LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9)
#define nonseekable_open(inode, filp) /* Nothing to do */
#endif
diff --git a/unmodified_drivers/linux-2.6/mkbuildtree b/unmodified_drivers/linux-2.6/mkbuildtree
index ce0a142447..ddcc13c75e 100644..100755
--- a/unmodified_drivers/linux-2.6/mkbuildtree
+++ b/unmodified_drivers/linux-2.6/mkbuildtree
@@ -22,6 +22,7 @@ done
ln -sf ${XL}/drivers/xen/core/gnttab.c platform-pci
ln -sf ${XL}/drivers/xen/core/features.c platform-pci
ln -sf ${XL}/drivers/xen/core/xen_proc.c xenbus
+ln -sf ${XL}/drivers/xen/core/reboot.c util
mkdir -p include
mkdir -p include/xen
@@ -30,7 +31,7 @@ mkdir -p include/asm
mkdir -p include/asm/xen
lndir -silent ${XL}/include/xen include/xen
-ln -sf ${XEN}/include/public include/xen/interface
+ln -nsf ${XEN}/include/public include/xen/interface
# Need to be quite careful here: we don't want the files we link in to
# risk overriding the native Linux ones (in particular, system.h must
diff --git a/unmodified_drivers/linux-2.6/platform-pci/evtchn.c b/unmodified_drivers/linux-2.6/platform-pci/evtchn.c
index 4bd9592754..d07250add5 100644
--- a/unmodified_drivers/linux-2.6/platform-pci/evtchn.c
+++ b/unmodified_drivers/linux-2.6/platform-pci/evtchn.c
@@ -167,11 +167,17 @@ irqreturn_t evtchn_interrupt(int irq, void *dev_id, struct pt_regs *regs)
l2 = s->evtchn_pending[l1i] & ~s->evtchn_mask[l1i];
}
}
+
+ /* Make sure the hypervisor has a chance to notice that the
+ upcall_pending condition has been cleared, so that we don't
+ try and reinject the interrupt again. */
+ (void)HYPERVISOR_xen_version(0, NULL);
+
return IRQ_HANDLED;
}
void force_evtchn_callback(void)
{
- evtchn_interrupt(0, NULL, NULL);
+ (void)HYPERVISOR_xen_version(0, NULL);
}
EXPORT_SYMBOL(force_evtchn_callback);
diff --git a/unmodified_drivers/linux-2.6/util/Kbuild b/unmodified_drivers/linux-2.6/util/Kbuild
new file mode 100644
index 0000000000..35495d8194
--- /dev/null
+++ b/unmodified_drivers/linux-2.6/util/Kbuild
@@ -0,0 +1,3 @@
+include $(M)/overrides.mk
+
+obj-m := reboot.o
diff --git a/unmodified_drivers/linux-2.6/util/Makefile b/unmodified_drivers/linux-2.6/util/Makefile
new file mode 100644
index 0000000000..64e7acd194
--- /dev/null
+++ b/unmodified_drivers/linux-2.6/util/Makefile
@@ -0,0 +1,3 @@
+ifneq ($(KERNELRELEASE),)
+include $(src)/Kbuild
+endif
diff --git a/xen/COPYING b/xen/COPYING
index fb8fcc90fc..43f972ee5e 100644
--- a/xen/COPYING
+++ b/xen/COPYING
@@ -9,15 +9,17 @@ Foundation, but the instance of code that it refers to (the Xen
virtual machine monitor) is copyrighted by me and others who actually
wrote it.
-Further note that the guest-OS interfacing header files, which
-includes all files within the subdirectory include/public, are
-*not* covered by the GPL but by a much weaker license:
- include/public/COPYING
-
-Also note that the only valid version of the GPL as far as Xen is
-concerned is _this_ particular version of the license (i.e., *only*
-v2, not v2.2 or v3.x or whatever), unless explicitly otherwise
-stated.
+A few files are licensed under both GPL and a weaker BSD-style
+license. This includes all files within the subdirectory
+include/public, as described in include/public/COPYING. All such files
+include the non-GPL license text as a source-code comment. Although
+the license text refers generically to "the software", the non-GPL
+license applies *only* to those source files that explicitly include
+the non-GPL license text.
+
+Note that the only valid version of the GPL as far as Xen is concerned
+is _this_ particular version of the license (i.e., *only* v2, not v2.2
+or v3.x or whatever), unless explicitly otherwise stated.
-- Keir Fraser (on behalf of the Xen team)
diff --git a/xen/arch/ia64/vmx/mmio.c b/xen/arch/ia64/vmx/mmio.c
index d605e828f0..04d1c68d74 100644
--- a/xen/arch/ia64/vmx/mmio.c
+++ b/xen/arch/ia64/vmx/mmio.c
@@ -214,8 +214,8 @@ static void low_mmio_access(VCPU *vcpu, u64 pa, u64 *val, size_t s, int dir)
p->count = 1;
p->dir = dir;
if(dir==IOREQ_WRITE) //write;
- p->u.data = *val;
- p->pdata_valid = 0;
+ p->data = *val;
+ p->data_is_ptr = 0;
p->type = 1;
p->df = 0;
@@ -227,7 +227,7 @@ static void low_mmio_access(VCPU *vcpu, u64 pa, u64 *val, size_t s, int dir)
}else
vmx_send_assist_req(v);
if(dir==IOREQ_READ){ //read
- *val=p->u.data;
+ *val=p->data;
}
return;
}
@@ -249,8 +249,8 @@ static void legacy_io_access(VCPU *vcpu, u64 pa, u64 *val, size_t s, int dir)
p->count = 1;
p->dir = dir;
if(dir==IOREQ_WRITE) //write;
- p->u.data = *val;
- p->pdata_valid = 0;
+ p->data = *val;
+ p->data_is_ptr = 0;
p->type = 0;
p->df = 0;
@@ -258,15 +258,15 @@ static void legacy_io_access(VCPU *vcpu, u64 pa, u64 *val, size_t s, int dir)
vmx_send_assist_req(v);
if(dir==IOREQ_READ){ //read
- *val=p->u.data;
+ *val=p->data;
}
#ifdef DEBUG_PCI
if(dir==IOREQ_WRITE)
if(p->addr == 0xcf8UL)
- printk("Write 0xcf8, with val [0x%lx]\n", p->u.data);
+ printk("Write 0xcf8, with val [0x%lx]\n", p->data);
else
if(p->addr == 0xcfcUL)
- printk("Read 0xcfc, with val [0x%lx]\n", p->u.data);
+ printk("Read 0xcfc, with val [0x%lx]\n", p->data);
#endif //DEBUG_PCI
return;
}
diff --git a/xen/arch/ia64/vmx/vlsapic.c b/xen/arch/ia64/vmx/vlsapic.c
index 5ab03e4730..3a30e6f1ed 100644
--- a/xen/arch/ia64/vmx/vlsapic.c
+++ b/xen/arch/ia64/vmx/vlsapic.c
@@ -95,7 +95,7 @@ static int vmx_vcpu_unpend_interrupt(VCPU *vcpu, uint8_t vector)
int ret;
if (vector & ~0xff) {
- DPRINTK("vmx_vcpu_pend_interrupt: bad vector\n");
+ dprintk(XENLOG_WARNING, "vmx_vcpu_pend_interrupt: bad vector\n");
return -1;
}
@@ -324,64 +324,26 @@ void vtm_domain_in(VCPU *vcpu)
*/
#ifdef V_IOSAPIC_READY
-/* Assist to check virtual interrupt lines */
-void vmx_virq_line_assist(struct vcpu *v)
-{
- global_iodata_t *spg = &get_sp(v->domain)->sp_global;
- uint16_t *virq_line, irqs;
-
- virq_line = &spg->pic_irr;
- if (*virq_line) {
- do {
- irqs = *(volatile uint16_t*)virq_line;
- } while ((uint16_t)cmpxchg(virq_line, irqs, 0) != irqs);
- hvm_vioapic_do_irqs(v->domain, irqs);
- }
-
- virq_line = &spg->pic_clear_irr;
- if (*virq_line) {
- do {
- irqs = *(volatile uint16_t*)virq_line;
- } while ((uint16_t)cmpxchg(virq_line, irqs, 0) != irqs);
- hvm_vioapic_do_irqs_clear(v->domain, irqs);
- }
-}
-
-void vmx_virq_line_init(struct domain *d)
+int vlapic_match_logical_addr(struct vlapic *vlapic, uint16_t dest)
{
- global_iodata_t *spg = &get_sp(d)->sp_global;
-
- spg->pic_elcr = 0xdef8; /* Level/Edge trigger mode */
- spg->pic_irr = 0;
- spg->pic_last_irr = 0;
- spg->pic_clear_irr = 0;
-}
-
-int ioapic_match_logical_addr(hvm_vioapic_t *s, int number, uint16_t dest)
-{
- return (VLAPIC_ID(s->lapic_info[number]) == dest);
+ return (VLAPIC_ID(vlapic) == dest);
}
struct vlapic* apic_round_robin(struct domain *d,
- uint8_t dest_mode,
uint8_t vector,
uint32_t bitmap)
{
- uint8_t bit;
- hvm_vioapic_t *s;
+ uint8_t bit = 0;
if (!bitmap) {
printk("<apic_round_robin> no bit on bitmap\n");
return NULL;
}
- s = &d->arch.vmx_platform.vioapic;
- for (bit = 0; bit < s->lapic_count; bit++) {
- if (bitmap & (1 << bit))
- return s->lapic_info[bit];
- }
+ while (!(bitmap & (1 << bit)))
+ bit++;
- return NULL;
+ return vcpu_vlapic(d->vcpu[bit]);
}
#endif
@@ -408,9 +370,8 @@ void vlsapic_reset(VCPU *vcpu)
#ifdef V_IOSAPIC_READY
vcpu->arch.arch_vmx.vlapic.vcpu = vcpu;
- hvm_vioapic_add_lapic(&vcpu->arch.arch_vmx.vlapic, vcpu);
#endif
- DPRINTK("VLSAPIC inservice base=%p\n", &VLSAPIC_INSVC(vcpu,0) );
+ dprintk(XENLOG_INFO, "VLSAPIC inservice base=%p\n", &VLSAPIC_INSVC(vcpu,0) );
}
/*
@@ -539,7 +500,7 @@ int vmx_vcpu_pend_interrupt(VCPU *vcpu, uint8_t vector)
int ret;
if (vector & ~0xff) {
- DPRINTK("vmx_vcpu_pend_interrupt: bad vector\n");
+ gdprintk(XENLOG_INFO, "vmx_vcpu_pend_interrupt: bad vector\n");
return -1;
}
local_irq_save(spsr);
diff --git a/xen/arch/ia64/vmx/vmx_hypercall.c b/xen/arch/ia64/vmx/vmx_hypercall.c
index bc3c6e7cab..2252ad6401 100644
--- a/xen/arch/ia64/vmx/vmx_hypercall.c
+++ b/xen/arch/ia64/vmx/vmx_hypercall.c
@@ -79,7 +79,7 @@ do_hvm_op(unsigned long op, XEN_GUEST_HANDLE(void) arg)
}
default:
- DPRINTK("Bad HVM op %ld.\n", op);
+ gdprintk(XENLOG_INFO, "Bad HVM op %ld.\n", op);
rc = -ENOSYS;
}
return rc;
diff --git a/xen/arch/ia64/vmx/vmx_init.c b/xen/arch/ia64/vmx/vmx_init.c
index f0aba60d7b..d13118b8cf 100644
--- a/xen/arch/ia64/vmx/vmx_init.c
+++ b/xen/arch/ia64/vmx/vmx_init.c
@@ -276,8 +276,8 @@ static void vmx_create_event_channels(struct vcpu *v)
p = get_vio(v->domain, o->vcpu_id);
o->arch.arch_vmx.xen_port = p->vp_eport =
alloc_unbound_xen_event_channel(o, 0);
- DPRINTK("Allocated port %d for hvm.\n",
- o->arch.arch_vmx.xen_port);
+ gdprintk(XENLOG_INFO, "Allocated port %d for hvm.\n",
+ o->arch.arch_vmx.xen_port);
}
}
}
@@ -321,8 +321,6 @@ vmx_final_setup_guest(struct vcpu *v)
vlsapic_reset(v);
vtm_init(v);
- /* One more step to enable interrupt assist */
- set_bit(ARCH_VMX_INTR_ASSIST, &v->arch.arch_vmx.flags);
/* Set up guest 's indicator for VTi domain*/
set_bit(ARCH_VMX_DOMAIN, &v->arch.arch_vmx.flags);
}
@@ -459,11 +457,8 @@ void vmx_setup_platform(struct domain *d)
/* initiate spinlock for pass virq */
spin_lock_init(&d->arch.arch_vmx.virq_assist_lock);
- /* Initialize the virtual interrupt lines */
- vmx_virq_line_init(d);
-
/* Initialize iosapic model within hypervisor */
- hvm_vioapic_init(d);
+ vioapic_init(d);
}
void vmx_do_launch(struct vcpu *v)
diff --git a/xen/arch/ia64/vmx/vmx_process.c b/xen/arch/ia64/vmx/vmx_process.c
index 855c7f4ccf..3526175cbd 100644
--- a/xen/arch/ia64/vmx/vmx_process.c
+++ b/xen/arch/ia64/vmx/vmx_process.c
@@ -209,10 +209,6 @@ void leave_hypervisor_tail(struct pt_regs *regs)
// if (user_regs != regs)
// printk("WARNING: checking pending interrupt in nested interrupt!!!\n");
- /* VMX Domain N has other interrupt source, saying DM */
- if (test_bit(ARCH_VMX_INTR_ASSIST, &v->arch.arch_vmx.flags))
- vmx_intr_assist(v);
-
/* FIXME: Check event pending indicator, and set
* pending bit if necessary to inject back to guest.
* Should be careful about window between this check
diff --git a/xen/arch/ia64/vmx/vmx_support.c b/xen/arch/ia64/vmx/vmx_support.c
index 70b9dd7321..cdb4c51be0 100644
--- a/xen/arch/ia64/vmx/vmx_support.c
+++ b/xen/arch/ia64/vmx/vmx_support.c
@@ -49,7 +49,7 @@ void vmx_io_assist(struct vcpu *v)
p = &vio->vp_ioreq;
if (p->state == STATE_IORESP_READY) {
- p->state = STATE_INVALID;
+ p->state = STATE_IOREQ_NONE;
}
else {
/* Can't block here, for the same reason as other places to
@@ -60,33 +60,12 @@ void vmx_io_assist(struct vcpu *v)
}
}
-/*
- * VMX domainN has two types of interrupt source: lsapic model within
- * HV, and device model within domain 0 (service OS). There're another
- * pending array in share page, manipulated by device model directly.
- * To conform to VT-i spec, we have to sync pending bits in shared page
- * into VPD. This has to be done before checking pending interrupt at
- * resume to guest. For domain 0, all the interrupt sources come from
- * HV, which then doesn't require this assist.
- */
-void vmx_intr_assist(struct vcpu *v)
-{
-#ifdef V_IOSAPIC_READY
- /* Confirm virtual interrupt line signals, and set pending bits in vpd */
- if (spin_trylock(&v->domain->arch.arch_vmx.virq_assist_lock)) {
- vmx_virq_line_assist(v);
- spin_unlock(&v->domain->arch.arch_vmx.virq_assist_lock);
- }
-#endif
- return;
-}
-
void vmx_send_assist_req(struct vcpu *v)
{
ioreq_t *p;
p = &get_vio(v->domain, v->vcpu_id)->vp_ioreq;
- if (unlikely(p->state != STATE_INVALID)) {
+ if (unlikely(p->state != STATE_IOREQ_NONE)) {
/* This indicates a bug in the device model. Crash the
domain. */
printk("Device model set bad IO state %d.\n", p->state);
diff --git a/xen/arch/ia64/xen/dom0_ops.c b/xen/arch/ia64/xen/dom0_ops.c
index d9ca611dbc..7bf0500cb8 100644
--- a/xen/arch/ia64/xen/dom0_ops.c
+++ b/xen/arch/ia64/xen/dom0_ops.c
@@ -320,7 +320,8 @@ do_dom0vp_op(unsigned long cmd,
case IA64_DOM0VP_phystomach:
ret = ____lookup_domain_mpa(d, arg0 << PAGE_SHIFT);
if (ret == INVALID_MFN) {
- DPRINTK("%s:%d INVALID_MFN ret: 0x%lx\n", __func__, __LINE__, ret);
+ dprintk(XENLOG_INFO, "%s: INVALID_MFN ret: 0x%lx\n",
+ __func__, ret);
} else {
ret = (ret & _PFN_MASK) >> PAGE_SHIFT;//XXX pte_pfn()
}
diff --git a/xen/arch/ia64/xen/domain.c b/xen/arch/ia64/xen/domain.c
index f1341fbf85..3b5a2c5ed0 100644
--- a/xen/arch/ia64/xen/domain.c
+++ b/xen/arch/ia64/xen/domain.c
@@ -275,40 +275,61 @@ void hlt_timer_fn(void *data)
vcpu_unblock(v);
}
-struct vcpu *alloc_vcpu_struct(struct domain *d, unsigned int vcpu_id)
+void relinquish_vcpu_resources(struct vcpu *v)
+{
+ if (HAS_PERVCPU_VHPT(v->domain))
+ pervcpu_vhpt_free(v);
+ if (v->arch.privregs != NULL) {
+ free_xenheap_pages(v->arch.privregs,
+ get_order_from_shift(XMAPPEDREGS_SHIFT));
+ v->arch.privregs = NULL;
+ }
+ kill_timer(&v->arch.hlt_timer);
+}
+
+struct vcpu *alloc_vcpu_struct(void)
{
struct vcpu *v;
struct thread_info *ti;
+ static int first_allocation = 1;
- /* Still keep idle vcpu0 static allocated at compilation, due
- * to some code from Linux still requires it in early phase.
- */
- if (is_idle_domain(d) && !vcpu_id)
- v = idle_vcpu[0];
- else {
- if ((v = alloc_xenheap_pages(KERNEL_STACK_SIZE_ORDER)) == NULL)
- return NULL;
- memset(v, 0, sizeof(*v));
-
- ti = alloc_thread_info(v);
- /* Clear thread_info to clear some important fields, like
- * preempt_count
- */
- memset(ti, 0, sizeof(struct thread_info));
- init_switch_stack(v);
+ if (first_allocation) {
+ first_allocation = 0;
+ /* Still keep idle vcpu0 static allocated at compilation, due
+ * to some code from Linux still requires it in early phase.
+ */
+ return idle_vcpu[0];
}
+ if ((v = alloc_xenheap_pages(KERNEL_STACK_SIZE_ORDER)) == NULL)
+ return NULL;
+ memset(v, 0, sizeof(*v));
+
+ ti = alloc_thread_info(v);
+ /* Clear thread_info to clear some important fields, like
+ * preempt_count
+ */
+ memset(ti, 0, sizeof(struct thread_info));
+ init_switch_stack(v);
+
+ return v;
+}
+
+void free_vcpu_struct(struct vcpu *v)
+{
+ free_xenheap_pages(v, KERNEL_STACK_SIZE_ORDER);
+}
+
+int vcpu_initialise(struct vcpu *v)
+{
+ struct domain *d = v->domain;
+ int rc, order, i;
+
if (!is_idle_domain(d)) {
if (!d->arch.is_vti) {
- int order;
- int i;
- // vti domain has its own vhpt policy.
- if (HAS_PERVCPU_VHPT(d)) {
- if (pervcpu_vhpt_alloc(v) < 0) {
- free_xenheap_pages(v, KERNEL_STACK_SIZE_ORDER);
- return NULL;
- }
- }
+ if (HAS_PERVCPU_VHPT(d))
+ if ((rc = pervcpu_vhpt_alloc(v)) != 0)
+ return rc;
/* Create privregs page only if not VTi. */
order = get_order_from_shift(XMAPPEDREGS_SHIFT);
@@ -344,34 +365,20 @@ struct vcpu *alloc_vcpu_struct(struct domain *d, unsigned int vcpu_id)
v->arch.breakimm = d->arch.breakimm;
v->arch.last_processor = INVALID_PROCESSOR;
}
- if (!VMX_DOMAIN(v)){
+
+ if (!VMX_DOMAIN(v))
init_timer(&v->arch.hlt_timer, hlt_timer_fn, v,
first_cpu(cpu_online_map));
- }
-
- return v;
-}
-void relinquish_vcpu_resources(struct vcpu *v)
-{
- if (HAS_PERVCPU_VHPT(v->domain))
- pervcpu_vhpt_free(v);
- if (v->arch.privregs != NULL) {
- free_xenheap_pages(v->arch.privregs,
- get_order_from_shift(XMAPPEDREGS_SHIFT));
- v->arch.privregs = NULL;
- }
- kill_timer(&v->arch.hlt_timer);
+ return 0;
}
-void free_vcpu_struct(struct vcpu *v)
+void vcpu_destroy(struct vcpu *v)
{
if (v->domain->arch.is_vti)
vmx_relinquish_vcpu_resources(v);
else
relinquish_vcpu_resources(v);
-
- free_xenheap_pages(v, KERNEL_STACK_SIZE_ORDER);
}
static void init_switch_stack(struct vcpu *v)
@@ -412,7 +419,7 @@ int arch_domain_create(struct domain *d)
#ifdef CONFIG_XEN_IA64_PERVCPU_VHPT
d->arch.has_pervcpu_vhpt = opt_pervcpu_vhpt;
- DPRINTK("%s:%d domain %d pervcpu_vhpt %d\n",
+ dprintk(XENLOG_WARNING, "%s:%d domain %d pervcpu_vhpt %d\n",
__func__, __LINE__, d->domain_id, d->arch.has_pervcpu_vhpt);
#endif
if (tlb_track_create(d) < 0)
@@ -681,7 +688,8 @@ int shadow_mode_control(struct domain *d, xen_domctl_shadow_op_t *sc)
//struct vcpu *v;
if (unlikely(d == current->domain)) {
- DPRINTK("Don't try to do a shadow op on yourself!\n");
+ gdprintk(XENLOG_INFO,
+ "Don't try to do a shadow op on yourself!\n");
return -EINVAL;
}
@@ -1175,6 +1183,6 @@ void sync_vcpu_execstate(struct vcpu *v)
static void parse_dom0_mem(char *s)
{
- dom0_size = parse_size_and_unit(s);
+ dom0_size = parse_size_and_unit(s, NULL);
}
custom_param("dom0_mem", parse_dom0_mem);
diff --git a/xen/arch/ia64/xen/irq.c b/xen/arch/ia64/xen/irq.c
index 156c09d5f1..6211e9b418 100644
--- a/xen/arch/ia64/xen/irq.c
+++ b/xen/arch/ia64/xen/irq.c
@@ -377,7 +377,8 @@ int pirq_guest_bind(struct vcpu *v, int irq, int will_share)
{
if ( desc->action != NULL )
{
- DPRINTK("Cannot bind IRQ %d to guest. In use by '%s'.\n",
+ gdprintk(XENLOG_INFO,
+ "Cannot bind IRQ %d to guest. In use by '%s'.\n",
irq, desc->action->name);
rc = -EBUSY;
goto out;
@@ -386,7 +387,9 @@ int pirq_guest_bind(struct vcpu *v, int irq, int will_share)
action = xmalloc(irq_guest_action_t);
if ( (desc->action = (struct irqaction *)action) == NULL )
{
- DPRINTK("Cannot bind IRQ %d to guest. Out of memory.\n", irq);
+ gdprintk(XENLOG_INFO,
+ "Cannot bind IRQ %d to guest. Out of memory.\n",
+ irq);
rc = -ENOMEM;
goto out;
}
@@ -410,7 +413,8 @@ int pirq_guest_bind(struct vcpu *v, int irq, int will_share)
}
else if ( !will_share || !action->shareable )
{
- DPRINTK("Cannot bind IRQ %d to guest. Will not share with others.\n",
+ gdprintk(XENLOG_INFO,
+ "Cannot bind IRQ %d to guest. Will not share with others.\n",
irq);
rc = -EBUSY;
goto out;
@@ -418,7 +422,9 @@ int pirq_guest_bind(struct vcpu *v, int irq, int will_share)
if ( action->nr_guests == IRQ_MAX_GUESTS )
{
- DPRINTK("Cannot bind IRQ %d to guest. Already at max share.\n", irq);
+ gdprintk(XENLOG_INFO,
+ "Cannot bind IRQ %d to guest. Already at max share.\n",
+ irq);
rc = -EBUSY;
goto out;
}
diff --git a/xen/arch/ia64/xen/mm.c b/xen/arch/ia64/xen/mm.c
index 28f70acbf9..40d91f22a3 100644
--- a/xen/arch/ia64/xen/mm.c
+++ b/xen/arch/ia64/xen/mm.c
@@ -229,7 +229,8 @@ try_to_clear_PGC_allocate(struct domain* d, struct page_info* page)
if (unlikely(!(x & PGC_allocated)) || unlikely(_nd != _d)) {
struct domain* nd = unpickle_domptr(_nd);
if (nd == NULL) {
- DPRINTK("gnttab_transfer: Bad page %p: ed=%p(%u) 0x%x, "
+ gdprintk(XENLOG_INFO, "gnttab_transfer: "
+ "Bad page %p: ed=%p(%u) 0x%x, "
"sd=%p 0x%x,"
" caf=%016lx, taf=%" PRtype_info "\n",
(void *) page_to_mfn(page),
@@ -444,7 +445,8 @@ u64 translate_domain_pte(u64 pteval, u64 address, u64 itir__, u64* logps,
arflags2 = pteval2 & _PAGE_AR_MASK;
if (arflags != _PAGE_AR_R && arflags2 == _PAGE_AR_R) {
#if 0
- DPRINTK("%s:%d "
+ dprintk(XENLOG_WARNING,
+ "%s:%d "
"pteval 0x%lx arflag 0x%lx address 0x%lx itir 0x%lx "
"pteval2 0x%lx arflags2 0x%lx mpaddr 0x%lx\n",
__func__, __LINE__,
@@ -978,8 +980,8 @@ efi_mmio(unsigned long physaddr, unsigned long size)
if (start <= physaddr && physaddr < end) {
if ((physaddr + size) > end) {
- DPRINTK("%s:%d physaddr 0x%lx size = 0x%lx\n",
- __func__, __LINE__, physaddr, size);
+ gdprintk(XENLOG_INFO, "%s: physaddr 0x%lx size = 0x%lx\n",
+ __func__, physaddr, size);
return 0;
}
@@ -1013,13 +1015,13 @@ assign_domain_mmio_page(struct domain *d,
unsigned long mpaddr, unsigned long size)
{
if (size == 0) {
- DPRINTK("%s: domain %p mpaddr 0x%lx size = 0x%lx\n",
+ gdprintk(XENLOG_INFO, "%s: domain %p mpaddr 0x%lx size = 0x%lx\n",
__func__, d, mpaddr, size);
}
if (!efi_mmio(mpaddr, size)) {
#ifndef NDEBUG
- DPRINTK("%s:%d domain %p mpaddr 0x%lx size = 0x%lx\n",
- __func__, __LINE__, d, mpaddr, size);
+ gdprintk(XENLOG_INFO, "%s: domain %p mpaddr 0x%lx size = 0x%lx\n",
+ __func__, d, mpaddr, size);
#endif
return -EINVAL;
}
@@ -1108,7 +1110,8 @@ assign_domain_page_cmpxchg_rel(struct domain* d, unsigned long mpaddr,
old_mfn = page_to_mfn(old_page);
old_pte = pfn_pte(old_mfn, __pgprot(old_prot));
if (!pte_present(old_pte)) {
- DPRINTK("%s: old_pte 0x%lx old_prot 0x%lx old_mfn 0x%lx\n",
+ gdprintk(XENLOG_INFO,
+ "%s: old_pte 0x%lx old_prot 0x%lx old_mfn 0x%lx\n",
__func__, pte_val(old_pte), old_prot, old_mfn);
return -EINVAL;
}
@@ -1124,7 +1127,8 @@ assign_domain_page_cmpxchg_rel(struct domain* d, unsigned long mpaddr,
goto again;
}
- DPRINTK("%s: old_pte 0x%lx old_prot 0x%lx old_mfn 0x%lx "
+ gdprintk(XENLOG_INFO,
+ "%s: old_pte 0x%lx old_prot 0x%lx old_mfn 0x%lx "
"ret_pte 0x%lx ret_mfn 0x%lx\n",
__func__,
pte_val(old_pte), old_prot, old_mfn,
@@ -1184,7 +1188,7 @@ zap_domain_page_one(struct domain *d, unsigned long mpaddr, unsigned long mfn)
goto again;
}
- DPRINTK("%s: old_pte 0x%lx old_arflags 0x%lx mfn 0x%lx "
+ gdprintk(XENLOG_INFO, "%s: old_pte 0x%lx old_arflags 0x%lx mfn 0x%lx "
"ret_pte 0x%lx ret_mfn 0x%lx\n",
__func__,
pte_val(old_pte), old_arflags, mfn,
@@ -1252,7 +1256,7 @@ dom0vp_add_physmap(struct domain* d, unsigned long gpfn, unsigned long mfn,
rd = dom_io;
break;
default:
- DPRINTK("d 0x%p domid %d "
+ gdprintk(XENLOG_INFO, "d 0x%p domid %d "
"pgfn 0x%lx mfn 0x%lx flags 0x%lx domid %d\n",
d, d->domain_id, gpfn, mfn, flags, domid);
return -ESRCH;
@@ -1325,14 +1329,16 @@ dom0vp_expose_p2m(struct domain* d,
(conv_start_gpfn % granule_pfn) != 0 ||
(assign_start_gpfn % granule_pfn) != 0 ||
(expose_num_pfn % granule_pfn) != 0) {
- DPRINTK("%s conv_start_gpfn 0x%016lx assign_start_gpfn 0x%016lx "
+ gdprintk(XENLOG_INFO,
+ "%s conv_start_gpfn 0x%016lx assign_start_gpfn 0x%016lx "
"expose_size 0x%016lx granulte_pfn 0x%016lx\n", __func__,
conv_start_gpfn, assign_start_gpfn, expose_size, granule_pfn);
return -EINVAL;
}
if (granule_pfn != PTRS_PER_PTE) {
- DPRINTK("%s granule_pfn 0x%016lx PTRS_PER_PTE 0x%016lx\n",
+ gdprintk(XENLOG_INFO,
+ "%s granule_pfn 0x%016lx PTRS_PER_PTE 0x%016lx\n",
__func__, granule_pfn, PTRS_PER_PTE);
return -ENOSYS;
}
@@ -1350,7 +1356,7 @@ dom0vp_expose_p2m(struct domain* d,
assign_pte = lookup_alloc_domain_pte(d, (assign_start_gpfn <<
PAGE_SHIFT) + i * sizeof(pte_t));
if (assign_pte == NULL) {
- DPRINTK("%s failed to allocate pte page\n", __func__);
+ gdprintk(XENLOG_INFO, "%s failed to allocate pte page\n", __func__);
return -ENOMEM;
}
@@ -1371,7 +1377,7 @@ dom0vp_expose_p2m(struct domain* d,
if (expose_p2m_page(d, (assign_start_gpfn << PAGE_SHIFT) +
i * sizeof(pte_t), virt_to_page(conv_pte)) < 0) {
- DPRINTK("%s failed to assign page\n", __func__);
+ gdprintk(XENLOG_INFO, "%s failed to assign page\n", __func__);
return -EAGAIN;
}
@@ -1390,7 +1396,7 @@ dom0vp_expose_p2m(struct domain* d,
}
if (expose_p2m_page(d, (assign_start_gpfn + i) << PAGE_SHIFT,
p2m_pte_zero_page) < 0) {
- DPRINTK("%s failed to assign zero-pte page\n", __func__);
+ gdprintk(XENLOG_INFO, "%s failed to assign zero-pte page\n", __func__);
return -EAGAIN;
}
}
@@ -1413,7 +1419,7 @@ create_grant_host_mapping(unsigned long gpaddr,
if (flags & (GNTMAP_device_map |
GNTMAP_application_map | GNTMAP_contains_pte)) {
- DPRINTK("%s: flags 0x%x\n", __func__, flags);
+ gdprintk(XENLOG_INFO, "%s: flags 0x%x\n", __func__, flags);
return GNTST_general_error;
}
@@ -1447,13 +1453,14 @@ destroy_grant_host_mapping(unsigned long gpaddr,
struct page_info* page;
if (flags & (GNTMAP_application_map | GNTMAP_contains_pte)) {
- DPRINTK("%s: flags 0x%x\n", __func__, flags);
+ gdprintk(XENLOG_INFO, "%s: flags 0x%x\n", __func__, flags);
return GNTST_general_error;
}
pte = lookup_noalloc_domain_pte(d, gpaddr);
if (pte == NULL) {
- DPRINTK("%s: gpaddr 0x%lx mfn 0x%lx\n", __func__, gpaddr, mfn);
+ gdprintk(XENLOG_INFO, "%s: gpaddr 0x%lx mfn 0x%lx\n",
+ __func__, gpaddr, mfn);
return GNTST_general_error;
}
@@ -1461,7 +1468,7 @@ destroy_grant_host_mapping(unsigned long gpaddr,
cur_arflags = pte_val(*pte) & ~_PAGE_PPN_MASK;
cur_pte = pfn_pte(mfn, __pgprot(cur_arflags));
if (!pte_present(cur_pte)) {
- DPRINTK("%s: gpaddr 0x%lx mfn 0x%lx cur_pte 0x%lx\n",
+ gdprintk(XENLOG_INFO, "%s: gpaddr 0x%lx mfn 0x%lx cur_pte 0x%lx\n",
__func__, gpaddr, mfn, pte_val(cur_pte));
return GNTST_general_error;
}
@@ -1469,7 +1476,8 @@ destroy_grant_host_mapping(unsigned long gpaddr,
old_pte = ptep_cmpxchg_rel(&d->arch.mm, gpaddr, pte, cur_pte, new_pte);
if (unlikely(!pte_present(old_pte))) {
- DPRINTK("%s: gpaddr 0x%lx mfn 0x%lx cur_pte 0x%lx old_pte 0x%lx\n",
+ gdprintk(XENLOG_INFO, "%s: gpaddr 0x%lx mfn 0x%lx"
+ " cur_pte 0x%lx old_pte 0x%lx\n",
__func__, gpaddr, mfn, pte_val(cur_pte), pte_val(old_pte));
return GNTST_general_error;
}
@@ -1477,7 +1485,8 @@ destroy_grant_host_mapping(unsigned long gpaddr,
if (pte_pfn(old_pte) == mfn) {
goto again;
}
- DPRINTK("%s gpaddr 0x%lx mfn 0x%lx cur_pte 0x%lx old_pte 0x%lx\n",
+ gdprintk(XENLOG_INFO, "%s gpaddr 0x%lx mfn 0x%lx cur_pte "
+ "0x%lx old_pte 0x%lx\n",
__func__, gpaddr, mfn, pte_val(cur_pte), pte_val(old_pte));
return GNTST_general_error;
}
@@ -1509,7 +1518,8 @@ steal_page(struct domain *d, struct page_info *page, unsigned int memflags)
u64 x, nx, y;
if (page_get_owner(page) != d) {
- DPRINTK("%s d 0x%p owner 0x%p\n", __func__, d, page_get_owner(page));
+ gdprintk(XENLOG_INFO, "%s d 0x%p owner 0x%p\n",
+ __func__, d, page_get_owner(page));
return -1;
}
@@ -1521,7 +1531,7 @@ steal_page(struct domain *d, struct page_info *page, unsigned int memflags)
new = alloc_domheap_page(d);
if (new == NULL) {
- DPRINTK("alloc_domheap_page() failed\n");
+ gdprintk(XENLOG_INFO, "alloc_domheap_page() failed\n");
return -1;
}
// zero out pages for security reasons
@@ -1545,7 +1555,8 @@ steal_page(struct domain *d, struct page_info *page, unsigned int memflags)
ret = assign_domain_page_cmpxchg_rel(d, gpfn << PAGE_SHIFT, page, new,
ASSIGN_writable);
if (ret < 0) {
- DPRINTK("assign_domain_page_cmpxchg_rel failed %d\n", ret);
+ gdprintk(XENLOG_INFO, "assign_domain_page_cmpxchg_rel failed %d\n",
+ ret);
set_gpfn_from_mfn(new_mfn, INVALID_M2P_ENTRY);
free_domheap_page(new);
return -1;
@@ -1582,7 +1593,8 @@ steal_page(struct domain *d, struct page_info *page, unsigned int memflags)
unlikely(_nd != _d)) {
struct domain* nd = unpickle_domptr(_nd);
if (nd == NULL) {
- DPRINTK("gnttab_transfer: Bad page %p: ed=%p(%u) 0x%x, "
+ gdprintk(XENLOG_INFO, "gnttab_transfer: "
+ "Bad page %p: ed=%p(%u) 0x%x, "
"sd=%p 0x%x,"
" caf=%016lx, taf=%" PRtype_info
" memflags 0x%x\n",
@@ -1593,7 +1605,8 @@ steal_page(struct domain *d, struct page_info *page, unsigned int memflags)
page->u.inuse.type_info,
memflags);
} else {
- DPRINTK("gnttab_transfer: Bad page %p: ed=%p(%u) 0x%x, "
+ gdprintk(XENLOG_WARNING, "gnttab_transfer: "
+ "Bad page %p: ed=%p(%u) 0x%x, "
"sd=%p(%u) 0x%x,"
" caf=%016lx, taf=%" PRtype_info
" memflags 0x%x\n",
@@ -1670,24 +1683,24 @@ domain_page_flush(struct domain* d, unsigned long mpaddr,
switch (tlb_track_search_and_remove(d->arch.tlb_track,
ptep, old_pte, &entry)) {
case TLB_TRACK_NOT_TRACKED:
- // DPRINTK("%s TLB_TRACK_NOT_TRACKED\n", __func__);
+ // dprintk(XENLOG_WARNING, "%s TLB_TRACK_NOT_TRACKED\n", __func__);
domain_flush_vtlb_all();
break;
case TLB_TRACK_NOT_FOUND:
/* do nothing */
- // DPRINTK("%s TLB_TRACK_NOT_FOUND\n", __func__);
+ // dprintk(XENLOG_WARNING, "%s TLB_TRACK_NOT_FOUND\n", __func__);
break;
case TLB_TRACK_FOUND:
- // DPRINTK("%s TLB_TRACK_FOUND\n", __func__);
+ // dprintk(XENLOG_WARNING, "%s TLB_TRACK_FOUND\n", __func__);
domain_flush_vtlb_track_entry(d, entry);
tlb_track_free_entry(d->arch.tlb_track, entry);
break;
case TLB_TRACK_MANY:
- DPRINTK("%s TLB_TRACK_MANY\n", __func__);
+ gdprintk(XENLOG_INFO, "%s TLB_TRACK_MANY\n", __func__);
domain_flush_vtlb_all();
break;
case TLB_TRACK_AGAIN:
- DPRINTK("%s TLB_TRACK_AGAIN\n", __func__);
+ gdprintk(XENLOG_ERR, "%s TLB_TRACK_AGAIN\n", __func__);
BUG();
break;
}
diff --git a/xen/arch/ia64/xen/tlb_track.c b/xen/arch/ia64/xen/tlb_track.c
index d403abd495..07816a4acb 100644
--- a/xen/arch/ia64/xen/tlb_track.c
+++ b/xen/arch/ia64/xen/tlb_track.c
@@ -44,13 +44,14 @@ tlb_track_allocate_entries(struct tlb_track* tlb_track)
BUG_ON(tlb_track->num_free > 0);
if (tlb_track->num_entries >= tlb_track->limit) {
- DPRINTK("%s: num_entries %d limit %d\n",
+ dprintk(XENLOG_WARNING, "%s: num_entries %d limit %d\n",
__func__, tlb_track->num_entries, tlb_track->limit);
return -ENOMEM;
}
entry_page = alloc_domheap_page(NULL);
if (entry_page == NULL) {
- DPRINTK("%s: domheap page failed. num_entries %d limit %d\n",
+ dprintk(XENLOG_WARNING,
+ "%s: domheap page failed. num_entries %d limit %d\n",
__func__, tlb_track->num_entries, tlb_track->limit);
return -ENOMEM;
}
diff --git a/xen/arch/ia64/xen/vcpu.c b/xen/arch/ia64/xen/vcpu.c
index 63d91576b7..bc5784fff0 100644
--- a/xen/arch/ia64/xen/vcpu.c
+++ b/xen/arch/ia64/xen/vcpu.c
@@ -1544,8 +1544,9 @@ vcpu_get_domain_bundle(VCPU * vcpu, REGS * regs, u64 gip,
// Last itc.i value is cached to PSCBX(vcpu, itlb).
tr = PSCBX(vcpu, itlb);
if (vcpu_match_tr_entry(&tr, gip, rid)) {
- //DPRINTK("%s gip 0x%lx gpip 0x%lx\n", __func__,
- // gip, gpip);
+ //dprintk(XENLOG_WARNING,
+ // "%s gip 0x%lx gpip 0x%lx\n", __func__,
+ // gip, gpip);
goto found;
}
trp = vcpu_tr_lookup(vcpu, gip, rid, 1);
@@ -1571,7 +1572,7 @@ vcpu_get_domain_bundle(VCPU * vcpu, REGS * regs, u64 gip,
set_metaphysical_rr0();
}
if (bundle->i64[0] == 0 && bundle->i64[1] == 0) {
- DPRINTK("%s gip 0x%lx\n", __func__, gip);
+ dprintk(XENLOG_INFO, "%s gip 0x%lx\n", __func__, gip);
return 0;
}
return 1;
diff --git a/xen/arch/ia64/xen/xencomm.c b/xen/arch/ia64/xen/xencomm.c
index 6ef781b0ea..8f6cec8cb7 100644
--- a/xen/arch/ia64/xen/xencomm.c
+++ b/xen/arch/ia64/xen/xencomm.c
@@ -345,6 +345,11 @@ xencomm_add_offset(
unsigned int chunksz;
unsigned int chunk_skip;
+ if (dest_paddr == XENCOMM_INVALID) {
+ i++;
+ continue;
+ }
+
pgoffset = dest_paddr % PAGE_SIZE;
chunksz = PAGE_SIZE - pgoffset;
@@ -356,6 +361,8 @@ xencomm_add_offset(
desc->address[i] += chunk_skip;
}
bytes -= chunk_skip;
+
+ i++;
}
return handle;
}
diff --git a/xen/arch/ia64/xen/xensetup.c b/xen/arch/ia64/xen/xensetup.c
index 94e65cfd97..76f28e0d15 100644
--- a/xen/arch/ia64/xen/xensetup.c
+++ b/xen/arch/ia64/xen/xensetup.c
@@ -422,7 +422,7 @@ void start_kernel(void)
scheduler_init();
idle_vcpu[0] = (struct vcpu*) ia64_r13;
- idle_domain = domain_create(IDLE_DOMAIN_ID);
+ idle_domain = domain_create(IDLE_DOMAIN_ID, 0);
if ( (idle_domain == NULL) || (alloc_vcpu(idle_domain, 0, 0) == NULL) )
BUG();
@@ -502,11 +502,11 @@ printk("num_online_cpus=%d, max_cpus=%d\n",num_online_cpus(),max_cpus);
expose_p2m_init();
/* Create initial domain 0. */
- dom0 = domain_create(0);
+ dom0 = domain_create(0, 0);
if ( (dom0 == NULL) || (alloc_vcpu(dom0, 0, 0) == NULL) )
panic("Error creating domain 0\n");
- set_bit(_DOMF_privileged, &dom0->domain_flags);
+ dom0->is_privileged = 1;
/*
* We're going to setup domain0 using the module(s) that we stashed safely
diff --git a/xen/arch/powerpc/domain.c b/xen/arch/powerpc/domain.c
index c3c4420dec..636713a8b7 100644
--- a/xen/arch/powerpc/domain.c
+++ b/xen/arch/powerpc/domain.c
@@ -109,27 +109,28 @@ void machine_restart(char * __unused)
while(1);
}
-struct vcpu *alloc_vcpu_struct(struct domain *d, unsigned int vcpu_id)
+struct vcpu *alloc_vcpu_struct(void)
{
struct vcpu *v;
-
- if ( (v = xmalloc(struct vcpu)) == NULL )
- return NULL;
-
- memset(v, 0, sizeof(*v));
- v->vcpu_id = vcpu_id;
-
+ if ( (v = xmalloc(struct vcpu)) != NULL )
+ memset(v, 0, sizeof(*v));
return v;
}
void free_vcpu_struct(struct vcpu *v)
{
- BUG_ON(v->next_in_list != NULL);
- if ( v->vcpu_id != 0 )
- v->domain->vcpu[v->vcpu_id - 1]->next_in_list = NULL;
xfree(v);
}
+int vcpu_initialise(struct vcpu *v)
+{
+ return 0;
+}
+
+void vcpu_destroy(struct vcpu *v)
+{
+}
+
int arch_set_info_guest(struct vcpu *v, vcpu_guest_context_t *c)
{
memcpy(&v->arch.ctxt, &c->user_regs, sizeof(c->user_regs));
diff --git a/xen/arch/powerpc/domain_build.c b/xen/arch/powerpc/domain_build.c
index 7858cb3c50..cc439ae51d 100644
--- a/xen/arch/powerpc/domain_build.c
+++ b/xen/arch/powerpc/domain_build.c
@@ -40,7 +40,7 @@ static void parse_dom0_mem(char *s)
{
unsigned long long bytes;
- bytes = parse_size_and_unit(s);
+ bytes = parse_size_and_unit(s, NULL);
dom0_nrpages = bytes >> PAGE_SHIFT;
}
custom_param("dom0_mem", parse_dom0_mem);
diff --git a/xen/arch/powerpc/mm.c b/xen/arch/powerpc/mm.c
index 568e8d94f2..1df18669d3 100644
--- a/xen/arch/powerpc/mm.c
+++ b/xen/arch/powerpc/mm.c
@@ -268,7 +268,7 @@ int allocate_rma(struct domain *d, unsigned int order)
d->arch.rma_page = alloc_domheap_pages(d, order, 0);
if (d->arch.rma_page == NULL) {
- DPRINTK("Could not allocate order=%d RMA for domain %u\n",
+ gdprintk(XENLOG_INFO, "Could not allocate order=%d RMA for domain %u\n",
order, d->domain_id);
return -ENOMEM;
}
@@ -316,8 +316,7 @@ ulong pfn2mfn(struct domain *d, ulong pfn, int *type)
int t = PFN_TYPE_NONE;
/* quick tests first */
- if (test_bit(_DOMF_privileged, &d->domain_flags) &&
- cpu_io_mfn(pfn)) {
+ if (d->is_privileged && cpu_io_mfn(pfn)) {
t = PFN_TYPE_IO;
mfn = pfn;
} else {
@@ -341,8 +340,7 @@ ulong pfn2mfn(struct domain *d, ulong pfn, int *type)
if (t == PFN_TYPE_NONE) {
/* This hack allows dom0 to map all memory, necessary to
* initialize domU state. */
- if (test_bit(_DOMF_privileged, &d->domain_flags) &&
- mfn_valid(pfn)) {
+ if (d->is_privileged && mfn_valid(pfn)) {
struct page_info *pg;
/* page better be allocated to some domain but not the caller */
diff --git a/xen/arch/powerpc/papr/xlate.c b/xen/arch/powerpc/papr/xlate.c
index 01a0e89664..7ea5a39d5f 100644
--- a/xen/arch/powerpc/papr/xlate.c
+++ b/xen/arch/powerpc/papr/xlate.c
@@ -174,7 +174,7 @@ static void h_enter(struct cpu_user_regs *regs)
if (mtype == PFN_TYPE_IO) {
/* only a privilaged dom can access outside IO space */
- if ( !test_bit(_DOMF_privileged, &d->domain_flags) ) {
+ if ( !d->is_privileged ) {
regs->gprs[3] = H_Privilege;
printk("%s: unprivileged access to physical page: 0x%lx\n",
__func__, pfn);
diff --git a/xen/arch/powerpc/setup.c b/xen/arch/powerpc/setup.c
index 83c0aca028..5c839f698e 100644
--- a/xen/arch/powerpc/setup.c
+++ b/xen/arch/powerpc/setup.c
@@ -157,7 +157,7 @@ static void __init start_of_day(void)
scheduler_init();
/* create idle domain */
- idle_domain = domain_create(IDLE_DOMAIN_ID);
+ idle_domain = domain_create(IDLE_DOMAIN_ID, 0);
if ((idle_domain == NULL) || (alloc_vcpu(idle_domain, 0, 0) == NULL))
BUG();
set_current(idle_domain->vcpu[0]);
@@ -342,7 +342,7 @@ static void __init __start_xen(multiboot_info_t *mbi)
start_of_day();
/* Create initial domain 0. */
- dom0 = domain_create(0);
+ dom0 = domain_create(0, 0);
if (dom0 == NULL)
panic("Error creating domain 0\n");
dom0->max_pages = ~0U;
@@ -355,8 +355,9 @@ static void __init __start_xen(multiboot_info_t *mbi)
* need to make sure Dom0's vVCPU 0 is pinned to the CPU */
dom0->vcpu[0]->cpu_affinity = cpumask_of_cpu(0);
- set_bit(_DOMF_privileged, &dom0->domain_flags);
- /* post-create hooks sets security label */
+ dom0->is_privileged = 1;
+
+ /* Post-create hook sets security label. */
acm_post_domain0_create(dom0->domain_id);
cmdline = (char *)(mod[0].string ? __va((ulong)mod[0].string) : NULL);
diff --git a/xen/arch/powerpc/shadow.c b/xen/arch/powerpc/shadow.c
index 3694d27e77..3d33433c0f 100644
--- a/xen/arch/powerpc/shadow.c
+++ b/xen/arch/powerpc/shadow.c
@@ -120,14 +120,14 @@ int shadow_domctl(struct domain *d,
{
if ( unlikely(d == current->domain) )
{
- DPRINTK("Don't try to do a shadow op on yourself!\n");
+ gdprintk(XENLOG_INFO, "Don't try to do a shadow op on yourself!\n");
return -EINVAL;
}
switch ( sc->op )
{
case XEN_DOMCTL_SHADOW_OP_OFF:
- DPRINTK("Shadow is mandatory!\n");
+ gdprintk(XENLOG_INFO, "Shadow is mandatory!\n");
return -EINVAL;
case XEN_DOMCTL_SHADOW_OP_GET_ALLOCATION:
diff --git a/xen/arch/powerpc/usercopy.c b/xen/arch/powerpc/usercopy.c
index 48ab7579de..d653ed1a05 100644
--- a/xen/arch/powerpc/usercopy.c
+++ b/xen/arch/powerpc/usercopy.c
@@ -249,6 +249,11 @@ int xencomm_add_offset(void *handle, unsigned int bytes)
unsigned int chunksz;
unsigned int chunk_skip;
+ if (dest_paddr == XENCOMM_INVALID) {
+ i++;
+ continue;
+ }
+
pgoffset = dest_paddr % PAGE_SIZE;
chunksz = PAGE_SIZE - pgoffset;
@@ -260,6 +265,8 @@ int xencomm_add_offset(void *handle, unsigned int bytes)
desc->address[i] += chunk_skip;
}
bytes -= chunk_skip;
+
+ i++;
}
return 0;
}
diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c
index 3e0a2f82ae..6c4ea37ae1 100644
--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -114,53 +114,66 @@ void dump_pageframe_info(struct domain *d)
}
}
-struct vcpu *alloc_vcpu_struct(struct domain *d, unsigned int vcpu_id)
+struct vcpu *alloc_vcpu_struct(void)
{
struct vcpu *v;
+ if ( (v = xmalloc(struct vcpu)) != NULL )
+ memset(v, 0, sizeof(*v));
+ return v;
+}
- if ( (v = xmalloc(struct vcpu)) == NULL )
- return NULL;
+void free_vcpu_struct(struct vcpu *v)
+{
+ xfree(v);
+}
- memset(v, 0, sizeof(*v));
+int vcpu_initialise(struct vcpu *v)
+{
+ struct domain *d = v->domain;
+ int rc;
v->arch.flags = TF_kernel_mode;
- if ( is_idle_domain(d) )
+ if ( is_hvm_domain(d) )
{
- v->arch.schedule_tail = continue_idle_domain;
- v->arch.cr3 = __pa(idle_pg_table);
+ if ( (rc = hvm_vcpu_initialise(v)) != 0 )
+ return rc;
}
else
{
v->arch.schedule_tail = continue_nonidle_domain;
- }
+ v->arch.ctxt_switch_from = paravirt_ctxt_switch_from;
+ v->arch.ctxt_switch_to = paravirt_ctxt_switch_to;
- v->arch.ctxt_switch_from = paravirt_ctxt_switch_from;
- v->arch.ctxt_switch_to = paravirt_ctxt_switch_to;
+ if ( is_idle_domain(d) )
+ {
+ v->arch.schedule_tail = continue_idle_domain;
+ v->arch.cr3 = __pa(idle_pg_table);
+ }
+ }
v->arch.perdomain_ptes =
- d->arch.mm_perdomain_pt + (vcpu_id << GDT_LDT_VCPU_SHIFT);
+ d->arch.mm_perdomain_pt + (v->vcpu_id << GDT_LDT_VCPU_SHIFT);
pae_l3_cache_init(&v->arch.pae_l3_cache);
- return v;
+ return 0;
}
-void free_vcpu_struct(struct vcpu *v)
+void vcpu_destroy(struct vcpu *v)
{
- xfree(v);
}
int arch_domain_create(struct domain *d)
{
l1_pgentry_t gdt_l1e;
int vcpuid, pdpt_order;
- int i;
+ int i, rc = -ENOMEM;
pdpt_order = get_order_from_bytes(PDPT_L1_ENTRIES * sizeof(l1_pgentry_t));
d->arch.mm_perdomain_pt = alloc_xenheap_pages(pdpt_order);
if ( d->arch.mm_perdomain_pt == NULL )
- goto fail_nomem;
+ goto fail;
memset(d->arch.mm_perdomain_pt, 0, PAGE_SIZE << pdpt_order);
/*
@@ -185,7 +198,7 @@ int arch_domain_create(struct domain *d)
d->arch.mm_perdomain_l3 = alloc_xenheap_page();
if ( (d->arch.mm_perdomain_l2 == NULL) ||
(d->arch.mm_perdomain_l3 == NULL) )
- goto fail_nomem;
+ goto fail;
memset(d->arch.mm_perdomain_l2, 0, PAGE_SIZE);
for ( i = 0; i < (1 << pdpt_order); i++ )
@@ -212,30 +225,39 @@ int arch_domain_create(struct domain *d)
d->arch.ioport_caps =
rangeset_new(d, "I/O Ports", RANGESETF_prettyprint_hex);
if ( d->arch.ioport_caps == NULL )
- goto fail_nomem;
+ goto fail;
if ( (d->shared_info = alloc_xenheap_page()) == NULL )
- goto fail_nomem;
+ goto fail;
memset(d->shared_info, 0, PAGE_SIZE);
share_xen_page_with_guest(
virt_to_page(d->shared_info), d, XENSHARE_writable);
}
- return 0;
+ return is_hvm_domain(d) ? hvm_domain_initialise(d) : 0;
- fail_nomem:
+ fail:
free_xenheap_page(d->shared_info);
#ifdef __x86_64__
free_xenheap_page(d->arch.mm_perdomain_l2);
free_xenheap_page(d->arch.mm_perdomain_l3);
#endif
free_xenheap_pages(d->arch.mm_perdomain_pt, pdpt_order);
- return -ENOMEM;
+ return rc;
}
void arch_domain_destroy(struct domain *d)
{
+ struct vcpu *v;
+
+ if ( is_hvm_domain(d) )
+ {
+ for_each_vcpu ( d, v )
+ hvm_vcpu_destroy(v);
+ hvm_domain_destroy(d);
+ }
+
shadow_final_teardown(d);
free_xenheap_pages(
@@ -258,7 +280,7 @@ int arch_set_info_guest(
unsigned long cr3_pfn = INVALID_MFN;
int i, rc;
- if ( !(c->flags & VGCF_HVM_GUEST) )
+ if ( !is_hvm_vcpu(v) )
{
fixup_guest_stack_selector(c->user_regs.ss);
fixup_guest_stack_selector(c->kernel_ss);
@@ -272,15 +294,13 @@ int arch_set_info_guest(
for ( i = 0; i < 256; i++ )
fixup_guest_code_selector(c->trap_ctxt[i].cs);
}
- else if ( !hvm_enabled )
- return -EINVAL;
clear_bit(_VCPUF_fpu_initialised, &v->vcpu_flags);
- if ( c->flags & VGCF_I387_VALID )
+ if ( c->flags & VGCF_i387_valid )
set_bit(_VCPUF_fpu_initialised, &v->vcpu_flags);
v->arch.flags &= ~TF_kernel_mode;
- if ( (c->flags & VGCF_IN_KERNEL) || (c->flags & VGCF_HVM_GUEST) )
+ if ( (c->flags & VGCF_in_kernel) || is_hvm_vcpu(v)/*???*/ )
v->arch.flags |= TF_kernel_mode;
memcpy(&v->arch.guest_context, c, sizeof(*c));
@@ -291,7 +311,7 @@ int arch_set_info_guest(
init_int80_direct_trap(v);
- if ( !(c->flags & VGCF_HVM_GUEST) )
+ if ( !is_hvm_vcpu(v) )
{
/* IOPL privileges are virtualised. */
v->arch.iopl = (v->arch.guest_context.user_regs.eflags >> 12) & 3;
@@ -300,7 +320,7 @@ int arch_set_info_guest(
/* Ensure real hardware interrupts are enabled. */
v->arch.guest_context.user_regs.eflags |= EF_IE;
}
- else if ( test_bit(_VCPUF_initialised, &v->vcpu_flags) )
+ else
{
hvm_load_cpu_guest_regs(v, &v->arch.guest_context.user_regs);
}
@@ -316,24 +336,13 @@ int arch_set_info_guest(
if ( v->vcpu_id == 0 )
d->vm_assist = c->vm_assist;
- if ( !(c->flags & VGCF_HVM_GUEST) )
+ if ( !is_hvm_vcpu(v) )
{
- cr3_pfn = gmfn_to_mfn(d, xen_cr3_to_pfn(c->ctrlreg[3]));
- v->arch.guest_table = pagetable_from_pfn(cr3_pfn);
- }
-
- if ( (rc = (int)set_gdt(v, c->gdt_frames, c->gdt_ents)) != 0 )
- return rc;
+ if ( (rc = (int)set_gdt(v, c->gdt_frames, c->gdt_ents)) != 0 )
+ return rc;
- if ( c->flags & VGCF_HVM_GUEST )
- {
- v->arch.guest_table = pagetable_null();
+ cr3_pfn = gmfn_to_mfn(d, xen_cr3_to_pfn(c->ctrlreg[3]));
- if ( !hvm_initialize_guest_resources(v) )
- return -EINVAL;
- }
- else
- {
if ( shadow_mode_refcounts(d)
? !get_page(mfn_to_page(cr3_pfn), d)
: !get_page_and_type(mfn_to_page(cr3_pfn), d,
@@ -342,6 +351,8 @@ int arch_set_info_guest(
destroy_gdt(v);
return -EINVAL;
}
+
+ v->arch.guest_table = pagetable_from_pfn(cr3_pfn);
}
/* Shadow: make sure the domain has enough shadow memory to
@@ -564,7 +575,8 @@ static void load_segments(struct vcpu *n)
put_user(regs->r11, rsp-10) |
put_user(regs->rcx, rsp-11) )
{
- DPRINTK("Error while creating failsafe callback frame.\n");
+ gdprintk(XENLOG_ERR, "Error while creating failsafe "
+ "callback frame.\n");
domain_crash(n->domain);
}
@@ -744,7 +756,7 @@ void context_switch(struct vcpu *prev, struct vcpu *next)
/* Re-enable interrupts before restoring state which may fault. */
local_irq_enable();
- if ( !hvm_guest(next) )
+ if ( !is_hvm_vcpu(next) )
{
load_LDT(next);
load_segments(next);
@@ -834,7 +846,7 @@ unsigned long hypercall_create_continuation(
#if defined(__i386__)
regs->eax = op;
- if ( supervisor_mode_kernel || hvm_guest(current) )
+ if ( supervisor_mode_kernel || is_hvm_vcpu(current) )
regs->eip &= ~31; /* re-execute entire hypercall entry stub */
else
regs->eip -= 2; /* re-execute 'int 0x82' */
@@ -971,9 +983,6 @@ void domain_relinquish_resources(struct domain *d)
#endif
}
- if ( d->vcpu[0] && hvm_guest(d->vcpu[0]) )
- hvm_relinquish_guest_resources(d);
-
/* Tear down shadow mode stuff. */
shadow_teardown(d);
diff --git a/xen/arch/x86/domain_build.c b/xen/arch/x86/domain_build.c
index b8765be263..110d642a15 100644
--- a/xen/arch/x86/domain_build.c
+++ b/xen/arch/x86/domain_build.c
@@ -33,28 +33,45 @@
extern unsigned long initial_images_nrpages(void);
extern void discard_initial_images(void);
-static long dom0_nrpages;
+static long dom0_nrpages, dom0_min_nrpages, dom0_max_nrpages = LONG_MAX;
/*
- * dom0_mem:
- * If +ve:
- * * The specified amount of memory is allocated to domain 0.
- * If -ve:
- * * All of memory is allocated to domain 0, minus the specified amount.
- * If not specified:
- * * All of memory is allocated to domain 0, minus 1/16th which is reserved
- * for uses such as DMA buffers (the reservation is clamped to 128MB).
+ * dom0_mem=[min:<min_amt>,][max:<max_amt>,][<amt>]
+ *
+ * <min_amt>: The minimum amount of memory which should be allocated for dom0.
+ * <max_amt>: The maximum amount of memory which should be allocated for dom0.
+ * <amt>: The precise amount of memory to allocate for dom0.
+ *
+ * Notes:
+ * 1. <amt> is clamped from below by <min_amt> and from above by available
+ * memory and <max_amt>
+ * 2. <min_amt> is clamped from above by available memory and <max_amt>
+ * 3. <min_amt> is ignored if it is greater than <max_amt>
+ * 4. If <amt> is not specified, it is calculated as follows:
+ * "All of memory is allocated to domain 0, minus 1/16th which is reserved
+ * for uses such as DMA buffers (the reservation is clamped to 128MB)."
+ *
+ * Each value can be specified as positive or negative:
+ * If +ve: The specified amount is an absolute value.
+ * If -ve: The specified amount is subtracted from total available memory.
*/
+static long parse_amt(char *s, char **ps)
+{
+ long pages = parse_size_and_unit((*s == '-') ? s+1 : s, ps) >> PAGE_SHIFT;
+ return (*s == '-') ? -pages : pages;
+}
static void parse_dom0_mem(char *s)
{
- unsigned long long bytes;
- char *t = s;
- if ( *s == '-' )
- t++;
- bytes = parse_size_and_unit(t);
- dom0_nrpages = bytes >> PAGE_SHIFT;
- if ( *s == '-' )
- dom0_nrpages = -dom0_nrpages;
+ do {
+ if ( !strncmp(s, "min:", 4) )
+ dom0_min_nrpages = parse_amt(s+4, &s);
+ else if ( !strncmp(s, "max:", 4) )
+ dom0_max_nrpages = parse_amt(s+4, &s);
+ else
+ dom0_nrpages = parse_amt(s, &s);
+ if ( *s != ',' )
+ break;
+ } while ( *s++ == ',' );
}
custom_param("dom0_mem", parse_dom0_mem);
@@ -103,6 +120,35 @@ static struct page_info *alloc_chunk(struct domain *d, unsigned long max_pages)
return page;
}
+static unsigned long compute_dom0_nr_pages(void)
+{
+ unsigned long avail = avail_domheap_pages() + initial_images_nrpages();
+
+ /*
+ * If domain 0 allocation isn't specified, reserve 1/16th of available
+ * memory for things like DMA buffers. This reservation is clamped to
+ * a maximum of 128MB.
+ */
+ if ( dom0_nrpages == 0 )
+ {
+ dom0_nrpages = avail;
+ dom0_nrpages = min(dom0_nrpages / 16, 128L << (20 - PAGE_SHIFT));
+ dom0_nrpages = -dom0_nrpages;
+ }
+
+ /* Negative memory specification means "all memory - specified amount". */
+ if ( dom0_nrpages < 0 ) dom0_nrpages += avail;
+ if ( dom0_min_nrpages < 0 ) dom0_min_nrpages += avail;
+ if ( dom0_max_nrpages < 0 ) dom0_max_nrpages += avail;
+
+ /* Clamp dom0 memory according to min/max limits and available memory. */
+ dom0_nrpages = max(dom0_nrpages, dom0_min_nrpages);
+ dom0_nrpages = min(dom0_nrpages, dom0_max_nrpages);
+ dom0_nrpages = min(dom0_nrpages, (long)avail);
+
+ return dom0_nrpages;
+}
+
static void process_dom0_ioports_disable(void)
{
unsigned long io_from, io_to;
@@ -269,25 +315,7 @@ int construct_dom0(struct domain *d,
d->max_pages = ~0U;
- /*
- * If domain 0 allocation isn't specified, reserve 1/16th of available
- * memory for things like DMA buffers. This reservation is clamped to
- * a maximum of 128MB.
- */
- if ( dom0_nrpages == 0 )
- {
- dom0_nrpages = avail_domheap_pages() + initial_images_nrpages();
- dom0_nrpages = min(dom0_nrpages / 16, 128L << (20 - PAGE_SHIFT));
- dom0_nrpages = -dom0_nrpages;
- }
-
- /* Negative memory specification means "all memory - specified amount". */
- if ( dom0_nrpages < 0 )
- nr_pages = avail_domheap_pages() + initial_images_nrpages() +
- dom0_nrpages;
- else
- nr_pages = min(avail_domheap_pages() + initial_images_nrpages(),
- (unsigned long)dom0_nrpages);
+ nr_pages = compute_dom0_nr_pages();
if ( (rc = parseelfimage(&dsi)) != 0 )
return rc;
diff --git a/xen/arch/x86/domctl.c b/xen/arch/x86/domctl.c
index ede75b31ea..d78e3b18ce 100644
--- a/xen/arch/x86/domctl.c
+++ b/xen/arch/x86/domctl.c
@@ -214,7 +214,7 @@ long arch_do_domctl(
int i;
struct domain *d = find_domain_by_id(domctl->domain);
unsigned long max_pfns = domctl->u.getmemlist.max_pfns;
- unsigned long mfn, gmfn;
+ unsigned long mfn;
struct list_head *list_ent;
ret = -EINVAL;
@@ -224,45 +224,18 @@ long arch_do_domctl(
spin_lock(&d->page_alloc_lock);
- if ( hvm_guest(d->vcpu[0]) && shadow_mode_translate(d) )
+ list_ent = d->page_list.next;
+ for ( i = 0; (i < max_pfns) && (list_ent != &d->page_list); i++ )
{
- /* HVM domain: scan P2M to get guaranteed physmap order. */
- for ( i = 0, gmfn = 0;
- (i < max_pfns) && (i < d->tot_pages);
- i++, gmfn++ )
+ mfn = page_to_mfn(list_entry(
+ list_ent, struct page_info, list));
+ if ( copy_to_guest_offset(domctl->u.getmemlist.buffer,
+ i, &mfn, 1) )
{
- if ( unlikely(i == (HVM_BELOW_4G_MMIO_START>>PAGE_SHIFT)) )
- {
- /* skip MMIO range */
- gmfn += HVM_BELOW_4G_MMIO_LENGTH >> PAGE_SHIFT;
- }
- mfn = gmfn_to_mfn(d, gmfn);
- if ( copy_to_guest_offset(domctl->u.getmemlist.buffer,
- i, &mfn, 1) )
- {
- ret = -EFAULT;
- break;
- }
- }
- }
- else
- {
- /* Other guests: return in order of ownership list. */
- list_ent = d->page_list.next;
- for ( i = 0;
- (i < max_pfns) && (list_ent != &d->page_list);
- i++ )
- {
- mfn = page_to_mfn(list_entry(
- list_ent, struct page_info, list));
- if ( copy_to_guest_offset(domctl->u.getmemlist.buffer,
- i, &mfn, 1) )
- {
- ret = -EFAULT;
- break;
- }
- list_ent = mfn_to_page(mfn)->list.next;
+ ret = -EFAULT;
+ break;
}
+ list_ent = mfn_to_page(mfn)->list.next;
}
spin_unlock(&d->page_alloc_lock);
@@ -321,7 +294,7 @@ void arch_getdomaininfo_ctxt(
{
memcpy(c, &v->arch.guest_context, sizeof(*c));
- if ( hvm_guest(v) )
+ if ( is_hvm_vcpu(v) )
{
hvm_store_cpu_guest_regs(v, &c->user_regs, c->ctrlreg);
}
@@ -334,11 +307,9 @@ void arch_getdomaininfo_ctxt(
c->flags = 0;
if ( test_bit(_VCPUF_fpu_initialised, &v->vcpu_flags) )
- c->flags |= VGCF_I387_VALID;
+ c->flags |= VGCF_i387_valid;
if ( guest_kernel_mode(v, &v->arch.guest_context.user_regs) )
- c->flags |= VGCF_IN_KERNEL;
- if ( hvm_guest(v) )
- c->flags |= VGCF_HVM_GUEST;
+ c->flags |= VGCF_in_kernel;
c->ctrlreg[3] = xen_pfn_to_cr3(pagetable_get_pfn(v->arch.guest_table));
diff --git a/xen/arch/x86/e820.c b/xen/arch/x86/e820.c
index bbc147139c..b12eb68c87 100644
--- a/xen/arch/x86/e820.c
+++ b/xen/arch/x86/e820.c
@@ -6,7 +6,7 @@
/* opt_mem: Limit of physical RAM. Any RAM beyond this point is ignored. */
unsigned long long opt_mem;
-static void parse_mem(char *s) { opt_mem = parse_size_and_unit(s); }
+static void parse_mem(char *s) { opt_mem = parse_size_and_unit(s, NULL); }
custom_param("mem", parse_mem);
struct e820map e820;
diff --git a/xen/arch/x86/extable.c b/xen/arch/x86/extable.c
index 053c767b39..c578c835f4 100644
--- a/xen/arch/x86/extable.c
+++ b/xen/arch/x86/extable.c
@@ -74,7 +74,7 @@ search_pre_exception_table(struct cpu_user_regs *regs)
unsigned long addr = (unsigned long)regs->eip;
unsigned long fixup = search_one_table(
__start___pre_ex_table, __stop___pre_ex_table-1, addr);
- DPRINTK("Pre-exception: %p -> %p\n", _p(addr), _p(fixup));
+ dprintk(XENLOG_INFO, "Pre-exception: %p -> %p\n", _p(addr), _p(fixup));
#ifdef PERF_COUNTERS
if ( fixup )
perfc_incrc(exception_fixed);
diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
index f950d05295..63c1235a39 100644
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -57,164 +57,19 @@ integer_param("hvm_debug", opt_hvm_debug_level);
struct hvm_function_table hvm_funcs;
-static void hvm_zap_mmio_range(
- struct domain *d, unsigned long pfn, unsigned long nr_pfn)
-{
- unsigned long i;
-
- ASSERT(d == current->domain);
-
- for ( i = 0; i < nr_pfn; i++ )
- {
- if ( pfn + i >= 0xfffff )
- break;
-
- if ( VALID_MFN(gmfn_to_mfn(d, pfn + i)) )
- guest_remove_page(d, pfn + i);
- }
-}
-
-static void e820_zap_iommu_callback(struct domain *d,
- struct e820entry *e,
- void *ign)
-{
- if ( e->type == E820_IO )
- hvm_zap_mmio_range(d, e->addr >> PAGE_SHIFT, e->size >> PAGE_SHIFT);
-}
-
-static void e820_foreach(struct domain *d,
- void (*cb)(struct domain *d,
- struct e820entry *e,
- void *data),
- void *data)
-{
- int i;
- unsigned char e820_map_nr;
- struct e820entry *e820entry;
- unsigned char *p;
- unsigned long mfn;
-
- mfn = gmfn_to_mfn(d, E820_MAP_PAGE >> PAGE_SHIFT);
- if ( mfn == INVALID_MFN )
- {
- printk("Can not find E820 memory map page for HVM domain.\n");
- domain_crash_synchronous();
- }
-
- p = map_domain_page(mfn);
- if ( p == NULL )
- {
- printk("Can not map E820 memory map page for HVM domain.\n");
- domain_crash_synchronous();
- }
-
- e820_map_nr = *(p + E820_MAP_NR_OFFSET);
- e820entry = (struct e820entry *)(p + E820_MAP_OFFSET);
-
- for ( i = 0; i < e820_map_nr; i++ )
- cb(d, e820entry + i, data);
-
- unmap_domain_page(p);
-}
-
-static void hvm_zap_iommu_pages(struct domain *d)
-{
- e820_foreach(d, e820_zap_iommu_callback, NULL);
-}
-
-static void e820_map_io_shared_callback(struct domain *d,
- struct e820entry *e,
- void *data)
-{
- unsigned long *mfn = data;
- if ( e->type == E820_SHARED_PAGE )
- {
- ASSERT(*mfn == INVALID_MFN);
- *mfn = gmfn_to_mfn(d, e->addr >> PAGE_SHIFT);
- }
-}
-
-static void e820_map_buffered_io_callback(struct domain *d,
- struct e820entry *e,
- void *data)
-{
- unsigned long *mfn = data;
- if ( e->type == E820_BUFFERED_IO ) {
- ASSERT(*mfn == INVALID_MFN);
- *mfn = gmfn_to_mfn(d, e->addr >> PAGE_SHIFT);
- }
-}
-
-void hvm_map_io_shared_pages(struct vcpu *v)
-{
- unsigned long mfn;
- void *p;
- struct domain *d = v->domain;
-
- if ( d->arch.hvm_domain.shared_page_va ||
- d->arch.hvm_domain.buffered_io_va )
- return;
-
- mfn = INVALID_MFN;
- e820_foreach(d, e820_map_io_shared_callback, &mfn);
-
- if ( mfn == INVALID_MFN )
- {
- printk("Can not find io request shared page for HVM domain.\n");
- domain_crash_synchronous();
- }
-
- p = map_domain_page_global(mfn);
- if ( p == NULL )
- {
- printk("Can not map io request shared page for HVM domain.\n");
- domain_crash_synchronous();
- }
-
- d->arch.hvm_domain.shared_page_va = (unsigned long)p;
-
- mfn = INVALID_MFN;
- e820_foreach(d, e820_map_buffered_io_callback, &mfn);
- if ( mfn != INVALID_MFN ) {
- p = map_domain_page_global(mfn);
- if ( p )
- d->arch.hvm_domain.buffered_io_va = (unsigned long)p;
- }
-}
-
-void hvm_create_event_channels(struct vcpu *v)
-{
- vcpu_iodata_t *p;
- struct vcpu *o;
-
- if ( v->vcpu_id == 0 ) {
- /* Ugly: create event channels for every vcpu when vcpu 0
- starts, so that they're available for ioemu to bind to. */
- for_each_vcpu(v->domain, o) {
- p = get_vio(v->domain, o->vcpu_id);
- o->arch.hvm_vcpu.xen_port = p->vp_eport =
- alloc_unbound_xen_event_channel(o, 0);
- DPRINTK("Allocated port %d for hvm.\n", o->arch.hvm_vcpu.xen_port);
- }
- }
-}
-
-
void hvm_stts(struct vcpu *v)
{
/* FPU state already dirty? Then no need to setup_fpu() lazily. */
- if ( test_bit(_VCPUF_fpu_dirtied, &v->vcpu_flags) )
- return;
-
- hvm_funcs.stts(v);
+ if ( !test_bit(_VCPUF_fpu_dirtied, &v->vcpu_flags) )
+ hvm_funcs.stts(v);
}
void hvm_set_guest_time(struct vcpu *v, u64 gtime)
{
u64 host_tsc;
-
+
rdtscll(host_tsc);
-
+
v->arch.hvm_vcpu.cache_tsc_offset = gtime - host_tsc;
hvm_funcs.set_tsc_offset(v, v->arch.hvm_vcpu.cache_tsc_offset);
}
@@ -222,99 +77,136 @@ void hvm_set_guest_time(struct vcpu *v, u64 gtime)
void hvm_do_resume(struct vcpu *v)
{
ioreq_t *p;
- struct periodic_time *pt =
- &v->domain->arch.hvm_domain.pl_time.periodic_tm;
+ struct periodic_time *pt = &v->domain->arch.hvm_domain.pl_time.periodic_tm;
hvm_stts(v);
- /* pick up the elapsed PIT ticks and re-enable pit_timer */
- if ( pt->enabled && pt->first_injected ) {
- if ( v->arch.hvm_vcpu.guest_time ) {
+ /* Pick up the elapsed PIT ticks and re-enable pit_timer. */
+ if ( pt->enabled && (v->vcpu_id == pt->bind_vcpu) && pt->first_injected )
+ {
+ if ( v->arch.hvm_vcpu.guest_time )
+ {
hvm_set_guest_time(v, v->arch.hvm_vcpu.guest_time);
v->arch.hvm_vcpu.guest_time = 0;
}
pickup_deactive_ticks(pt);
}
+ /* NB. Optimised for common case (p->state == STATE_IOREQ_NONE). */
p = &get_vio(v->domain, v->vcpu_id)->vp_ioreq;
- wait_on_xen_event_channel(v->arch.hvm_vcpu.xen_port,
- p->state != STATE_IOREQ_READY &&
- p->state != STATE_IOREQ_INPROCESS);
- switch ( p->state )
+ while ( p->state != STATE_IOREQ_NONE )
{
- case STATE_IORESP_READY:
- hvm_io_assist(v);
- break;
- case STATE_INVALID:
- break;
- default:
- printk("Weird HVM iorequest state %d.\n", p->state);
- domain_crash(v->domain);
+ switch ( p->state )
+ {
+ case STATE_IORESP_READY: /* IORESP_READY -> NONE */
+ hvm_io_assist(v);
+ break;
+ case STATE_IOREQ_READY: /* IOREQ_{READY,INPROCESS} -> IORESP_READY */
+ case STATE_IOREQ_INPROCESS:
+ wait_on_xen_event_channel(v->arch.hvm_vcpu.xen_port,
+ (p->state != STATE_IOREQ_READY) &&
+ (p->state != STATE_IOREQ_INPROCESS));
+ break;
+ default:
+ gdprintk(XENLOG_ERR, "Weird HVM iorequest state %d.\n", p->state);
+ domain_crash_synchronous();
+ }
}
}
-void hvm_release_assist_channel(struct vcpu *v)
+int hvm_domain_initialise(struct domain *d)
{
- free_xen_event_channel(v, v->arch.hvm_vcpu.xen_port);
-}
+ struct hvm_domain *platform = &d->arch.hvm_domain;
+ int rc;
+
+ if ( !hvm_enabled )
+ {
+ gdprintk(XENLOG_WARNING, "Attempt to create a HVM guest "
+ "on a non-VT/AMDV platform.\n");
+ return -EINVAL;
+ }
+
+ spin_lock_init(&d->arch.hvm_domain.pbuf_lock);
+ spin_lock_init(&d->arch.hvm_domain.round_robin_lock);
+ spin_lock_init(&d->arch.hvm_domain.buffered_io_lock);
+
+ rc = shadow_enable(d, SHM2_refcounts|SHM2_translate|SHM2_external);
+ if ( rc != 0 )
+ return rc;
+
+ pic_init(&platform->vpic, pic_irq_request, &platform->interrupt_request);
+ register_pic_io_hook(d);
+ vioapic_init(d);
-void hvm_setup_platform(struct domain* d)
+ return 0;
+}
+
+void hvm_domain_destroy(struct domain *d)
{
- struct hvm_domain *platform;
- struct vcpu *v=current;
+ kill_timer(&d->arch.hvm_domain.pl_time.periodic_tm.timer);
+ rtc_deinit(d);
+ pmtimer_deinit(d);
- if ( !hvm_guest(v) || (v->vcpu_id != 0) )
- return;
+ if ( d->arch.hvm_domain.shared_page_va )
+ unmap_domain_page_global(
+ (void *)d->arch.hvm_domain.shared_page_va);
- hvm_zap_iommu_pages(d);
+ if ( d->arch.hvm_domain.buffered_io_va )
+ unmap_domain_page_global((void *)d->arch.hvm_domain.buffered_io_va);
+}
- platform = &d->arch.hvm_domain;
- pic_init(&platform->vpic, pic_irq_request, &platform->interrupt_request);
- register_pic_io_hook();
+int hvm_vcpu_initialise(struct vcpu *v)
+{
+ struct hvm_domain *platform;
+ int rc;
- if ( hvm_apic_support(d) )
+ if ( (rc = vlapic_init(v)) != 0 )
+ return rc;
+
+ if ( (rc = hvm_funcs.vcpu_initialise(v)) != 0 )
{
- spin_lock_init(&d->arch.hvm_domain.round_robin_lock);
- hvm_vioapic_init(d);
+ vlapic_destroy(v);
+ return rc;
}
- spin_lock_init(&d->arch.hvm_domain.buffered_io_lock);
+ /* Create ioreq event channel. */
+ v->arch.hvm_vcpu.xen_port = alloc_unbound_xen_event_channel(v, 0);
+ if ( get_sp(v->domain) && get_vio(v->domain, v->vcpu_id) )
+ get_vio(v->domain, v->vcpu_id)->vp_eport =
+ v->arch.hvm_vcpu.xen_port;
+
+ if ( v->vcpu_id != 0 )
+ return 0;
+
+ /* XXX Below should happen in hvm_domain_initialise(). */
+ platform = &v->domain->arch.hvm_domain;
init_timer(&platform->pl_time.periodic_tm.timer,
pt_timer_fn, v, v->processor);
pit_init(v, cpu_khz);
rtc_init(v, RTC_PORT(0), RTC_IRQ);
pmtimer_init(v, ACPI_PM_TMR_BLK_ADDRESS);
+
+ /* Init guest TSC to start from zero. */
+ hvm_set_guest_time(v, 0);
+
+ return 0;
}
-void pic_irq_request(void *data, int level)
+void hvm_vcpu_destroy(struct vcpu *v)
{
- int *interrupt_request = data;
- *interrupt_request = level;
+ vlapic_destroy(v);
+ hvm_funcs.vcpu_destroy(v);
+
+ /* Event channel is already freed by evtchn_destroy(). */
+ /*free_xen_event_channel(v, v->arch.hvm_vcpu.xen_port);*/
}
-void hvm_pic_assist(struct vcpu *v)
+void pic_irq_request(void *data, int level)
{
- global_iodata_t *spg;
- u16 *virq_line, irqs;
- struct hvm_virpic *pic = &v->domain->arch.hvm_domain.vpic;
-
- spg = &get_sp(v->domain)->sp_global;
- virq_line = &spg->pic_clear_irr;
- if ( *virq_line ) {
- do {
- irqs = *(volatile u16*)virq_line;
- } while ( (u16)cmpxchg(virq_line,irqs, 0) != irqs );
- do_pic_irqs_clear(pic, irqs);
- }
- virq_line = &spg->pic_irr;
- if ( *virq_line ) {
- do {
- irqs = *(volatile u16*)virq_line;
- } while ( (u16)cmpxchg(virq_line,irqs, 0) != irqs );
- do_pic_irqs(pic, irqs);
- }
+ int *interrupt_request = data;
+ *interrupt_request = level;
}
u64 hvm_get_guest_time(struct vcpu *v)
@@ -328,15 +220,15 @@ u64 hvm_get_guest_time(struct vcpu *v)
int cpu_get_interrupt(struct vcpu *v, int *type)
{
int intno;
- struct hvm_virpic *s = &v->domain->arch.hvm_domain.vpic;
+ struct vpic *vpic = domain_vpic(v->domain);
unsigned long flags;
if ( (intno = cpu_get_apic_interrupt(v, type)) != -1 ) {
/* set irq request if a PIC irq is still pending */
/* XXX: improve that */
- spin_lock_irqsave(&s->lock, flags);
- pic_update_irq(s);
- spin_unlock_irqrestore(&s->lock, flags);
+ spin_lock_irqsave(&vpic->lock, flags);
+ pic_update_irq(vpic);
+ spin_unlock_irqrestore(&vpic->lock, flags);
return intno;
}
/* read the irq from the PIC */
@@ -352,7 +244,8 @@ static void hvm_vcpu_down(void)
struct domain *d = v->domain;
int online_count = 0;
- DPRINTK("DOM%d/VCPU%d: going offline.\n", d->domain_id, v->vcpu_id);
+ gdprintk(XENLOG_INFO, "DOM%d/VCPU%d: going offline.\n",
+ d->domain_id, v->vcpu_id);
/* Doesn't halt us immediately, but we'll never return to guest context. */
set_bit(_VCPUF_down, &v->vcpu_flags);
@@ -368,17 +261,14 @@ static void hvm_vcpu_down(void)
/* ... Shut down the domain if not. */
if ( online_count == 0 )
{
- DPRINTK("DOM%d: all CPUs offline -- powering off.\n", d->domain_id);
+ gdprintk(XENLOG_INFO, "DOM%d: all CPUs offline -- powering off.\n",
+ d->domain_id);
domain_shutdown(d, SHUTDOWN_poweroff);
}
}
void hvm_hlt(unsigned long rflags)
{
- struct vcpu *v = current;
- struct periodic_time *pt = &v->domain->arch.hvm_domain.pl_time.periodic_tm;
- s_time_t next_pt = -1, next_wakeup;
-
/*
* If we halt with interrupts disabled, that's a pretty sure sign that we
* want to shut down. In a real processor, NMIs are the only way to break
@@ -387,29 +277,19 @@ void hvm_hlt(unsigned long rflags)
if ( unlikely(!(rflags & X86_EFLAGS_IF)) )
return hvm_vcpu_down();
- if ( !v->vcpu_id )
- next_pt = get_scheduled(v, pt->irq, pt);
- next_wakeup = get_apictime_scheduled(v);
- if ( (next_pt != -1 && next_pt < next_wakeup) || next_wakeup == -1 )
- next_wakeup = next_pt;
- if ( next_wakeup != - 1 )
- set_timer(&current->arch.hvm_vcpu.hlt_timer, next_wakeup);
do_sched_op_compat(SCHEDOP_block, 0);
}
/*
* __hvm_copy():
* @buf = hypervisor buffer
- * @addr = guest virtual or physical address to copy to/from
+ * @addr = guest physical address to copy to/from
* @size = number of bytes to copy
* @dir = copy *to* guest (TRUE) or *from* guest (FALSE)?
- * @phy = interpret addr as physical (TRUE) or virtual (FALSE) address?
* Returns number of bytes failed to copy (0 == complete success).
*/
-static int __hvm_copy(
- void *buf, unsigned long addr, int size, int dir, int phy)
+static int __hvm_copy(void *buf, paddr_t addr, int size, int dir)
{
- struct vcpu *v = current;
unsigned long mfn;
char *p;
int count, todo;
@@ -419,9 +299,7 @@ static int __hvm_copy(
{
count = min_t(int, PAGE_SIZE - (addr & ~PAGE_MASK), todo);
- mfn = phy ?
- get_mfn_from_gpfn(addr >> PAGE_SHIFT) :
- mfn_x(sh_vcpu_gfn_to_mfn(v, shadow_gva_to_gfn(v, addr)));
+ mfn = get_mfn_from_gpfn(addr >> PAGE_SHIFT);
if ( mfn == INVALID_MFN )
return todo;
@@ -442,42 +320,42 @@ static int __hvm_copy(
return 0;
}
-int hvm_copy_to_guest_phys(unsigned long paddr, void *buf, int size)
+int hvm_copy_to_guest_phys(paddr_t paddr, void *buf, int size)
{
- return __hvm_copy(buf, paddr, size, 1, 1);
+ return __hvm_copy(buf, paddr, size, 1);
}
-int hvm_copy_from_guest_phys(void *buf, unsigned long paddr, int size)
+int hvm_copy_from_guest_phys(void *buf, paddr_t paddr, int size)
{
- return __hvm_copy(buf, paddr, size, 0, 1);
+ return __hvm_copy(buf, paddr, size, 0);
}
int hvm_copy_to_guest_virt(unsigned long vaddr, void *buf, int size)
{
- return __hvm_copy(buf, vaddr, size, 1, 0);
+ return __hvm_copy(buf, shadow_gva_to_gpa(current, vaddr), size, 1);
}
int hvm_copy_from_guest_virt(void *buf, unsigned long vaddr, int size)
{
- return __hvm_copy(buf, vaddr, size, 0, 0);
+ return __hvm_copy(buf, shadow_gva_to_gpa(current, vaddr), size, 0);
}
-/*
- * HVM specific printbuf. Mostly used for hvmloader chit-chat.
- */
+/* HVM specific printbuf. Mostly used for hvmloader chit-chat. */
void hvm_print_line(struct vcpu *v, const char c)
{
- int *index = &v->domain->arch.hvm_domain.pbuf_index;
- char *pbuf = v->domain->arch.hvm_domain.pbuf;
-
- if (*index == HVM_PBUF_SIZE-2 || c == '\n') {
- if (*index == HVM_PBUF_SIZE-2)
- pbuf[(*index)++] = c;
- pbuf[*index] = '\0';
- printk("(GUEST: %u) %s\n", v->domain->domain_id, pbuf);
- *index = 0;
- } else
- pbuf[(*index)++] = c;
+ struct hvm_domain *hd = &v->domain->arch.hvm_domain;
+
+ spin_lock(&hd->pbuf_lock);
+ hd->pbuf[hd->pbuf_idx++] = c;
+ if ( (hd->pbuf_idx == (sizeof(hd->pbuf) - 2)) || (c == '\n') )
+ {
+ if ( c != '\n' )
+ hd->pbuf[hd->pbuf_idx++] = '\n';
+ hd->pbuf[hd->pbuf_idx] = '\0';
+ printk(XENLOG_G_DEBUG "HVM%u: %s", v->domain->domain_id, hd->pbuf);
+ hd->pbuf_idx = 0;
+ }
+ spin_unlock(&hd->pbuf_lock);
}
typedef unsigned long hvm_hypercall_t(
@@ -490,7 +368,7 @@ typedef unsigned long hvm_hypercall_t(
#if defined(__i386__)
-static hvm_hypercall_t *hvm_hypercall_table[] = {
+static hvm_hypercall_t *hvm_hypercall_table[NR_hypercalls] = {
HYPERCALL(memory_op),
HYPERCALL(multicall),
HYPERCALL(xen_version),
@@ -508,7 +386,7 @@ void hvm_do_hypercall(struct cpu_user_regs *pregs)
if ( (pregs->eax >= NR_hypercalls) || !hvm_hypercall_table[pregs->eax] )
{
- DPRINTK("HVM vcpu %d:%d did a bad hypercall %d.\n",
+ gdprintk(XENLOG_WARNING, "HVM vcpu %d:%d did a bad hypercall %d.\n",
current->domain->domain_id, current->vcpu_id,
pregs->eax);
pregs->eax = -ENOSYS;
@@ -554,7 +432,7 @@ static long do_memory_op_compat32(int cmd, XEN_GUEST_HANDLE(void) arg)
}
default:
- DPRINTK("memory_op %d.\n", cmd);
+ gdprintk(XENLOG_WARNING, "memory_op %d.\n", cmd);
rc = -ENOSYS;
break;
}
@@ -587,7 +465,7 @@ void hvm_do_hypercall(struct cpu_user_regs *pregs)
pregs->rax = (uint32_t)pregs->eax; /* mask in case compat32 caller */
if ( (pregs->rax >= NR_hypercalls) || !hvm_hypercall64_table[pregs->rax] )
{
- DPRINTK("HVM vcpu %d:%d did a bad hypercall %ld.\n",
+ gdprintk(XENLOG_WARNING, "HVM vcpu %d:%d did a bad hypercall %ld.\n",
current->domain->domain_id, current->vcpu_id,
pregs->rax);
pregs->rax = -ENOSYS;
@@ -634,11 +512,11 @@ int hvm_bringup_ap(int vcpuid, int trampoline_vector)
struct vcpu_guest_context *ctxt;
int rc = 0;
- BUG_ON(!hvm_guest(bsp));
+ BUG_ON(!is_hvm_domain(d));
if ( bsp->vcpu_id != 0 )
{
- DPRINTK("Not calling hvm_bringup_ap from BSP context.\n");
+ gdprintk(XENLOG_ERR, "Not calling hvm_bringup_ap from BSP context.\n");
domain_crash_synchronous();
}
@@ -647,12 +525,18 @@ int hvm_bringup_ap(int vcpuid, int trampoline_vector)
if ( (ctxt = xmalloc(struct vcpu_guest_context)) == NULL )
{
- DPRINTK("Failed to allocate memory in hvm_bringup_ap.\n");
+ gdprintk(XENLOG_ERR,
+ "Failed to allocate memory in hvm_bringup_ap.\n");
return -ENOMEM;
}
hvm_init_ap_context(ctxt, vcpuid, trampoline_vector);
+ /* Sync AP's TSC with BSP's. */
+ v->arch.hvm_vcpu.cache_tsc_offset =
+ v->domain->vcpu[0]->arch.hvm_vcpu.cache_tsc_offset;
+ hvm_funcs.set_tsc_offset(v, v->arch.hvm_vcpu.cache_tsc_offset);
+
LOCK_BIGLOCK(d);
rc = -EEXIST;
if ( !test_bit(_VCPUF_initialised, &v->vcpu_flags) )
@@ -661,13 +545,14 @@ int hvm_bringup_ap(int vcpuid, int trampoline_vector)
if ( rc != 0 )
{
- DPRINTK("AP %d bringup failed in boot_vcpu %x.\n", vcpuid, rc);
+ gdprintk(XENLOG_ERR,
+ "AP %d bringup failed in boot_vcpu %x.\n", vcpuid, rc);
goto out;
}
if ( test_and_clear_bit(_VCPUF_down, &d->vcpu[vcpuid]->vcpu_flags) )
vcpu_wake(d->vcpu[vcpuid]);
- DPRINTK("AP %d bringup suceeded.\n", vcpuid);
+ gdprintk(XENLOG_INFO, "AP %d bringup suceeded.\n", vcpuid);
out:
xfree(ctxt);
@@ -686,6 +571,9 @@ long do_hvm_op(unsigned long op, XEN_GUEST_HANDLE(void) arg)
{
struct xen_hvm_param a;
struct domain *d;
+ struct vcpu *v;
+ unsigned long mfn;
+ void *p;
if ( copy_from_guest(&a, arg, 1) )
return -EFAULT;
@@ -709,8 +597,41 @@ long do_hvm_op(unsigned long op, XEN_GUEST_HANDLE(void) arg)
return -EPERM;
}
+ rc = -EINVAL;
+ if ( !is_hvm_domain(d) )
+ goto param_fail;
+
if ( op == HVMOP_set_param )
{
+ switch ( a.index )
+ {
+ case HVM_PARAM_IOREQ_PFN:
+ if ( d->arch.hvm_domain.shared_page_va )
+ goto param_fail;
+ mfn = gmfn_to_mfn(d, a.value);
+ if ( mfn == INVALID_MFN )
+ goto param_fail;
+ p = map_domain_page_global(mfn);
+ if ( p == NULL )
+ goto param_fail;
+ d->arch.hvm_domain.shared_page_va = (unsigned long)p;
+ /* Initialise evtchn port info if VCPUs already created. */
+ for_each_vcpu ( d, v )
+ get_vio(d, v->vcpu_id)->vp_eport =
+ v->arch.hvm_vcpu.xen_port;
+ break;
+ case HVM_PARAM_BUFIOREQ_PFN:
+ if ( d->arch.hvm_domain.buffered_io_va )
+ goto param_fail;
+ mfn = gmfn_to_mfn(d, a.value);
+ if ( mfn == INVALID_MFN )
+ goto param_fail;
+ p = map_domain_page_global(mfn);
+ if ( p == NULL )
+ goto param_fail;
+ d->arch.hvm_domain.buffered_io_va = (unsigned long)p;
+ break;
+ }
d->arch.hvm_domain.params[a.index] = a.value;
rc = 0;
}
@@ -720,13 +641,40 @@ long do_hvm_op(unsigned long op, XEN_GUEST_HANDLE(void) arg)
rc = copy_to_guest(arg, &a, 1) ? -EFAULT : 0;
}
+ param_fail:
+ put_domain(d);
+ break;
+ }
+
+ case HVMOP_set_irq_level:
+ {
+ struct xen_hvm_set_irq_level op;
+ struct domain *d;
+
+ if ( copy_from_guest(&op, arg, 1) )
+ return -EFAULT;
+
+ if ( !IS_PRIV(current->domain) )
+ return -EPERM;
+
+ d = find_domain_by_id(op.domid);
+ if ( d == NULL )
+ return -ESRCH;
+
+ rc = -EINVAL;
+ if ( is_hvm_domain(d) )
+ {
+ pic_set_irq(domain_vpic(d), op.irq, op.level);
+ rc = 0;
+ }
+
put_domain(d);
break;
}
default:
{
- DPRINTK("Bad HVM op %ld.\n", op);
+ gdprintk(XENLOG_WARNING, "Bad HVM op %ld.\n", op);
rc = -ENOSYS;
break;
}
diff --git a/xen/arch/x86/hvm/i8254.c b/xen/arch/x86/hvm/i8254.c
index 464ddee8f9..516c114907 100644
--- a/xen/arch/x86/hvm/i8254.c
+++ b/xen/arch/x86/hvm/i8254.c
@@ -374,9 +374,9 @@ void pit_init(struct vcpu *v, unsigned long cpu_khz)
s++; s->vcpu = v;
s++; s->vcpu = v;
- register_portio_handler(PIT_BASE, 4, handle_pit_io);
+ register_portio_handler(v->domain, PIT_BASE, 4, handle_pit_io);
/* register the speaker port */
- register_portio_handler(0x61, 1, handle_speaker_io);
+ register_portio_handler(v->domain, 0x61, 1, handle_speaker_io);
ticks_per_sec(v) = cpu_khz * (int64_t)1000;
#ifdef DEBUG_PIT
printk("HVM_PIT: guest frequency =%lld\n", (long long)ticks_per_sec(v));
@@ -392,17 +392,17 @@ static int handle_pit_io(ioreq_t *p)
struct PITState *vpit = &(v->domain->arch.hvm_domain.pl_time.vpit);
if (p->size != 1 ||
- p->pdata_valid ||
+ p->data_is_ptr ||
p->type != IOREQ_TYPE_PIO){
printk("HVM_PIT:wrong PIT IO!\n");
return 1;
}
if (p->dir == 0) {/* write */
- pit_ioport_write(vpit, p->addr, p->u.data);
+ pit_ioport_write(vpit, p->addr, p->data);
} else if (p->dir == 1) { /* read */
if ( (p->addr & 3) != 3 ) {
- p->u.data = pit_ioport_read(vpit, p->addr);
+ p->data = pit_ioport_read(vpit, p->addr);
} else {
printk("HVM_PIT: read A1:A0=3!\n");
}
@@ -434,16 +434,16 @@ static int handle_speaker_io(ioreq_t *p)
struct PITState *vpit = &(v->domain->arch.hvm_domain.pl_time.vpit);
if (p->size != 1 ||
- p->pdata_valid ||
+ p->data_is_ptr ||
p->type != IOREQ_TYPE_PIO){
printk("HVM_SPEAKER:wrong SPEAKER IO!\n");
return 1;
}
if (p->dir == 0) {/* write */
- speaker_ioport_write(vpit, p->addr, p->u.data);
+ speaker_ioport_write(vpit, p->addr, p->data);
} else if (p->dir == 1) {/* read */
- p->u.data = speaker_ioport_read(vpit, p->addr);
+ p->data = speaker_ioport_read(vpit, p->addr);
}
return 1;
diff --git a/xen/arch/x86/hvm/i8259.c b/xen/arch/x86/hvm/i8259.c
index 64689e4a8d..de61f96c8d 100644
--- a/xen/arch/x86/hvm/i8259.c
+++ b/xen/arch/x86/hvm/i8259.c
@@ -5,10 +5,10 @@
* Copyright (c) 2005 Intel Corperation
*
* 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
+ * 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
@@ -18,10 +18,11 @@
* 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.
+ * 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 <xen/config.h>
#include <xen/types.h>
#include <xen/mm.h>
@@ -34,6 +35,8 @@
#include <asm/hvm/support.h>
#include <asm/current.h>
+#define hw_error(x) ((void)0)
+
/* set irq level. If an edge is detected, then the IRR is set to 1 */
static inline void pic_set_irq1(PicState *s, int irq, int level)
{
@@ -108,92 +111,65 @@ static int pic_get_irq(PicState *s)
/* raise irq to CPU if necessary. must be called every time the active
irq may change */
/* XXX: should not export it, but it is needed for an APIC kludge */
-void pic_update_irq(struct hvm_virpic *s)
+void pic_update_irq(struct vpic *vpic)
{
int irq2, irq;
- ASSERT(spin_is_locked(&s->lock));
+ ASSERT(spin_is_locked(&vpic->lock));
/* first look at slave pic */
- irq2 = pic_get_irq(&s->pics[1]);
+ irq2 = pic_get_irq(&vpic->pics[1]);
if (irq2 >= 0) {
/* if irq request by slave pic, signal master PIC */
- pic_set_irq1(&s->pics[0], 2, 1);
- pic_set_irq1(&s->pics[0], 2, 0);
+ pic_set_irq1(&vpic->pics[0], 2, 1);
+ pic_set_irq1(&vpic->pics[0], 2, 0);
}
/* look at requested irq */
- irq = pic_get_irq(&s->pics[0]);
+ irq = pic_get_irq(&vpic->pics[0]);
if (irq >= 0) {
- s->irq_request(s->irq_request_opaque, 1);
+ vpic->irq_request(vpic->irq_request_opaque, 1);
}
}
void pic_set_xen_irq(void *opaque, int irq, int level)
{
- struct hvm_virpic *s = opaque;
+ struct vpic *vpic = opaque;
unsigned long flags;
PicState *ps;
- spin_lock_irqsave(&s->lock, flags);
+ spin_lock_irqsave(&vpic->lock, flags);
- hvm_vioapic_set_xen_irq(current->domain, irq, level);
+ vioapic_set_xen_irq(current->domain, irq, level);
/* Set it on the 8259s */
- ps = &s->pics[irq >> 3];
- if (!(ps->elcr & (1 << (irq & 7)))) {
- DPRINTK("edge-triggered override IRQ?\n");
- domain_crash(current->domain);
- }
+ ps = &vpic->pics[irq >> 3];
+ if (!(ps->elcr & (1 << (irq & 7))))
+ gdprintk(XENLOG_WARNING, "edge-triggered override IRQ?\n");
if (level) {
ps->irr_xen |= 1 << (irq & 7);
} else {
ps->irr_xen &= ~(1 << (irq & 7));
}
- pic_update_irq(s);
- spin_unlock_irqrestore(&s->lock, flags);
-}
-
-void pic_set_irq_new(void *opaque, int irq, int level)
-{
- struct hvm_virpic *s = opaque;
- unsigned long flags;
-
- spin_lock_irqsave(&s->lock, flags);
- hvm_vioapic_set_irq(current->domain, irq, level);
- pic_set_irq1(&s->pics[irq >> 3], irq & 7, level);
- pic_update_irq(s);
- spin_unlock_irqrestore(&s->lock, flags);
+ pic_update_irq(vpic);
+ spin_unlock_irqrestore(&vpic->lock, flags);
}
-void do_pic_irqs (struct hvm_virpic *s, uint16_t irqs)
+void pic_set_irq(struct vpic *vpic, int irq, int level)
{
unsigned long flags;
- spin_lock_irqsave(&s->lock, flags);
- s->pics[1].irr |= (uint8_t)(irqs >> 8);
- s->pics[0].irr |= (uint8_t) irqs;
- hvm_vioapic_do_irqs(current->domain, irqs);
- pic_update_irq(s);
- spin_unlock_irqrestore(&s->lock, flags);
-}
-
-void do_pic_irqs_clear (struct hvm_virpic *s, uint16_t irqs)
-{
- unsigned long flags;
+ if ( irq < 0 )
+ return;
- spin_lock_irqsave(&s->lock, flags);
- s->pics[1].irr &= ~(uint8_t)(irqs >> 8);
- s->pics[0].irr &= ~(uint8_t) irqs;
- hvm_vioapic_do_irqs_clear(current->domain, irqs);
- pic_update_irq(s);
- spin_unlock_irqrestore(&s->lock, flags);
-}
-
-/* obsolete function */
-void pic_set_irq(struct hvm_virpic *isa_pic, int irq, int level)
-{
- pic_set_irq_new(isa_pic, irq, level);
+ spin_lock_irqsave(&vpic->lock, flags);
+ vioapic_set_irq(vpic_domain(vpic), irq, level);
+ if ( irq < 16 )
+ {
+ pic_set_irq1(&vpic->pics[irq >> 3], irq & 7, level);
+ pic_update_irq(vpic);
+ }
+ spin_unlock_irqrestore(&vpic->lock, flags);
}
/* acknowledge interrupt 'irq' */
@@ -212,57 +188,39 @@ static inline void pic_intack(PicState *s, int irq)
s->irr &= ~(1 << irq);
}
-int pic_read_irq(struct hvm_virpic *s)
+static int pic_read_irq(struct vpic *vpic)
{
int irq, irq2, intno;
unsigned long flags;
- spin_lock_irqsave(&s->lock, flags);
- irq = pic_get_irq(&s->pics[0]);
+ spin_lock_irqsave(&vpic->lock, flags);
+ irq = pic_get_irq(&vpic->pics[0]);
if (irq >= 0) {
- pic_intack(&s->pics[0], irq);
+ pic_intack(&vpic->pics[0], irq);
if (irq == 2) {
- irq2 = pic_get_irq(&s->pics[1]);
+ irq2 = pic_get_irq(&vpic->pics[1]);
if (irq2 >= 0) {
- pic_intack(&s->pics[1], irq2);
+ pic_intack(&vpic->pics[1], irq2);
} else {
/* spurious IRQ on slave controller */
+ gdprintk(XENLOG_WARNING, "Spurious irq on slave i8259.\n");
irq2 = 7;
}
- intno = s->pics[1].irq_base + irq2;
+ intno = vpic->pics[1].irq_base + irq2;
irq = irq2 + 8;
} else {
- intno = s->pics[0].irq_base + irq;
+ intno = vpic->pics[0].irq_base + irq;
}
} else {
/* spurious IRQ on host controller */
irq = 7;
- intno = s->pics[0].irq_base + irq;
+ intno = vpic->pics[0].irq_base + irq;
+ gdprintk(XENLOG_WARNING, "Spurious irq on master i8259.\n");
}
- pic_update_irq(s);
- spin_unlock_irqrestore(&s->lock, flags);
-
- return intno;
-}
-
-static void update_shared_irr(struct hvm_virpic *s, PicState *c)
-{
- uint8_t *pl, *pe;
+ pic_update_irq(vpic);
+ spin_unlock_irqrestore(&vpic->lock, flags);
- ASSERT(spin_is_locked(&s->lock));
-
- get_sp(current->domain)->sp_global.pic_elcr =
- s->pics[0].elcr | ((u16)s->pics[1].elcr << 8);
- pl =(uint8_t*)&get_sp(current->domain)->sp_global.pic_last_irr;
- pe =(uint8_t*)&get_sp(current->domain)->sp_global.pic_elcr;
- if ( c == &s->pics[0] ) {
- *pl = c->last_irr;
- *pe = c->elcr;
- }
- else {
- *(pl+1) = c->last_irr;
- *(pe+1) = c->elcr;
- }
+ return intno;
}
static void pic_reset(void *opaque)
@@ -300,7 +258,6 @@ static void pic_ioport_write(void *opaque, uint32_t addr, uint32_t val)
if (val & 0x10) {
/* init */
pic_reset(s);
- update_shared_irr(s->pics_state, s);
/* deassert a pending interrupt */
s->pics_state->irq_request(s->pics_state->irq_request_opaque, 0);
s->init_state = 1;
@@ -432,24 +389,6 @@ static uint32_t pic_ioport_read(void *opaque, uint32_t addr1)
return ret;
}
-/* memory mapped interrupt status */
-/* XXX: may be the same than pic_read_rq() */
-uint32_t pic_intack_read(struct hvm_virpic *s)
-{
- int ret;
- unsigned long flags;
-
- spin_lock_irqsave(&s->lock, flags);
- ret = pic_poll_read(&s->pics[0], 0x00);
- if (ret == 2)
- ret = pic_poll_read(&s->pics[1], 0x80) + 8;
- /* Prepare for ISR read */
- s->pics[0].read_reg_select = 1;
- spin_unlock_irqrestore(&s->lock, flags);
-
- return ret;
-}
-
static void elcr_ioport_write(void *opaque, uint32_t addr, uint32_t val)
{
PicState *s = opaque;
@@ -477,28 +416,28 @@ static void pic_init1(int io_addr, int elcr_addr, PicState *s)
s->elcr = 0xff & s->elcr_mask;
}
-void pic_init(struct hvm_virpic *s, void (*irq_request)(void *, int),
+void pic_init(struct vpic *vpic, void (*irq_request)(void *, int),
void *irq_request_opaque)
{
unsigned long flags;
- memset(s, 0, sizeof(*s));
- spin_lock_init(&s->lock);
- s->pics[0].pics_state = s;
- s->pics[1].pics_state = s;
- s->pics[0].elcr_mask = 0xf8;
- s->pics[1].elcr_mask = 0xde;
- spin_lock_irqsave(&s->lock, flags);
- pic_init1(0x20, 0x4d0, &s->pics[0]);
- pic_init1(0xa0, 0x4d1, &s->pics[1]);
- spin_unlock_irqrestore(&s->lock, flags);
- s->irq_request = irq_request;
- s->irq_request_opaque = irq_request_opaque;
+ memset(vpic, 0, sizeof(*vpic));
+ spin_lock_init(&vpic->lock);
+ vpic->pics[0].pics_state = vpic;
+ vpic->pics[1].pics_state = vpic;
+ vpic->pics[0].elcr_mask = 0xf8;
+ vpic->pics[1].elcr_mask = 0xde;
+ spin_lock_irqsave(&vpic->lock, flags);
+ pic_init1(0x20, 0x4d0, &vpic->pics[0]);
+ pic_init1(0xa0, 0x4d1, &vpic->pics[1]);
+ spin_unlock_irqrestore(&vpic->lock, flags);
+ vpic->irq_request = irq_request;
+ vpic->irq_request_opaque = irq_request_opaque;
}
static int intercept_pic_io(ioreq_t *p)
{
- struct hvm_virpic *pic;
+ struct vpic *pic;
uint32_t data;
unsigned long flags;
@@ -507,13 +446,12 @@ static int intercept_pic_io(ioreq_t *p)
return 1;
}
- pic = &current->domain->arch.hvm_domain.vpic;
+ pic = domain_vpic(current->domain);
if ( p->dir == IOREQ_WRITE ) {
- if ( p->pdata_valid )
- (void)hvm_copy_from_guest_phys(
- &data, (unsigned long)p->u.pdata, p->size);
+ if ( p->data_is_ptr )
+ (void)hvm_copy_from_guest_phys(&data, p->data, p->size);
else
- data = p->u.data;
+ data = p->data;
spin_lock_irqsave(&pic->lock, flags);
pic_ioport_write((void*)&pic->pics[p->addr>>7],
(uint32_t) p->addr, (uint32_t) (data & 0xff));
@@ -524,18 +462,17 @@ static int intercept_pic_io(ioreq_t *p)
data = pic_ioport_read(
(void*)&pic->pics[p->addr>>7], (uint32_t) p->addr);
spin_unlock_irqrestore(&pic->lock, flags);
- if ( p->pdata_valid )
- (void)hvm_copy_to_guest_phys(
- (unsigned long)p->u.pdata, &data, p->size);
+ if ( p->data_is_ptr )
+ (void)hvm_copy_to_guest_phys(p->data, &data, p->size);
else
- p->u.data = (u64)data;
+ p->data = (u64)data;
}
return 1;
}
static int intercept_elcr_io(ioreq_t *p)
{
- struct hvm_virpic *s;
+ struct vpic *vpic;
uint32_t data;
unsigned long flags;
@@ -544,38 +481,34 @@ static int intercept_elcr_io(ioreq_t *p)
return 1;
}
- s = &current->domain->arch.hvm_domain.vpic;
+ vpic = domain_vpic(current->domain);
if ( p->dir == IOREQ_WRITE ) {
- if ( p->pdata_valid )
- (void)hvm_copy_from_guest_phys(
- &data, (unsigned long)p->u.pdata, p->size);
+ if ( p->data_is_ptr )
+ (void)hvm_copy_from_guest_phys(&data, p->data, p->size);
else
- data = p->u.data;
- spin_lock_irqsave(&s->lock, flags);
- elcr_ioport_write((void*)&s->pics[p->addr&1],
+ data = p->data;
+ spin_lock_irqsave(&vpic->lock, flags);
+ elcr_ioport_write((void*)&vpic->pics[p->addr&1],
(uint32_t) p->addr, (uint32_t)( data & 0xff));
- get_sp(current->domain)->sp_global.pic_elcr =
- s->pics[0].elcr | ((u16)s->pics[1].elcr << 8);
- spin_unlock_irqrestore(&s->lock, flags);
+ spin_unlock_irqrestore(&vpic->lock, flags);
}
else {
data = (u64) elcr_ioport_read(
- (void*)&s->pics[p->addr&1], (uint32_t) p->addr);
- if ( p->pdata_valid )
- (void)hvm_copy_to_guest_phys(
- (unsigned long)p->u.pdata, &data, p->size);
+ (void*)&vpic->pics[p->addr&1], (uint32_t) p->addr);
+ if ( p->data_is_ptr )
+ (void)hvm_copy_to_guest_phys(p->data, &data, p->size);
else
- p->u.data = (u64)data;
+ p->data = (u64)data;
}
return 1;
}
-void register_pic_io_hook (void)
+void register_pic_io_hook(struct domain *d)
{
- register_portio_handler(0x20, 2, intercept_pic_io);
- register_portio_handler(0x4d0, 1, intercept_elcr_io);
- register_portio_handler(0xa0, 2, intercept_pic_io);
- register_portio_handler(0x4d1, 1, intercept_elcr_io);
+ register_portio_handler(d, 0x20, 2, intercept_pic_io);
+ register_portio_handler(d, 0x4d0, 1, intercept_elcr_io);
+ register_portio_handler(d, 0xa0, 2, intercept_pic_io);
+ register_portio_handler(d, 0x4d1, 1, intercept_elcr_io);
}
@@ -583,7 +516,7 @@ void register_pic_io_hook (void)
int cpu_get_pic_interrupt(struct vcpu *v, int *type)
{
int intno;
- struct hvm_virpic *s = &v->domain->arch.hvm_domain.vpic;
+ struct vpic *vpic = domain_vpic(v->domain);
struct hvm_domain *plat = &v->domain->arch.hvm_domain;
if ( !vlapic_accept_pic_intr(v) )
@@ -593,7 +526,7 @@ int cpu_get_pic_interrupt(struct vcpu *v, int *type)
return -1;
/* read the irq from the PIC */
- intno = pic_read_irq(s);
+ intno = pic_read_irq(vpic);
*type = APIC_DM_EXTINT;
return intno;
}
@@ -608,10 +541,9 @@ int is_periodic_irq(struct vcpu *v, int irq, int type)
if (pt->irq == 0) { /* Is it pit irq? */
if (type == APIC_DM_EXTINT)
- vec = v->domain->arch.hvm_domain.vpic.pics[0].irq_base;
+ vec = domain_vpic(v->domain)->pics[0].irq_base;
else
- vec =
- v->domain->arch.hvm_domain.vioapic.redirtbl[0].RedirForm.vector;
+ vec = domain_vioapic(v->domain)->redirtbl[0].fields.vector;
if (irq == vec)
return 1;
@@ -619,10 +551,9 @@ int is_periodic_irq(struct vcpu *v, int irq, int type)
if (pt->irq == 8) { /* Or rtc irq? */
if (type == APIC_DM_EXTINT)
- vec = v->domain->arch.hvm_domain.vpic.pics[1].irq_base;
+ vec = domain_vpic(v->domain)->pics[1].irq_base;
else
- vec =
- v->domain->arch.hvm_domain.vioapic.redirtbl[8].RedirForm.vector;
+ vec = domain_vioapic(v->domain)->redirtbl[8].fields.vector;
if (irq == vec)
return is_rtc_periodic_irq(vrtc);
@@ -633,10 +564,10 @@ int is_periodic_irq(struct vcpu *v, int irq, int type)
int is_irq_enabled(struct vcpu *v, int irq)
{
- struct hvm_vioapic *vioapic = &v->domain->arch.hvm_domain.vioapic;
- struct hvm_virpic *vpic=&v->domain->arch.hvm_domain.vpic;
+ struct vioapic *vioapic = domain_vioapic(v->domain);
+ struct vpic *vpic = domain_vpic(v->domain);
- if (vioapic->redirtbl[irq].RedirForm.mask == 0)
+ if (vioapic->redirtbl[irq].fields.mask == 0)
return 1;
if ( irq & 8 ) {
diff --git a/xen/arch/x86/hvm/instrlen.c b/xen/arch/x86/hvm/instrlen.c
index 073edafcca..a1c6c64af1 100644
--- a/xen/arch/x86/hvm/instrlen.c
+++ b/xen/arch/x86/hvm/instrlen.c
@@ -317,7 +317,7 @@ done_prefixes:
if ( modrm_mod == 3 )
{
- DPRINTK("Cannot parse ModRM.mod == 3.\n");
+ gdprintk(XENLOG_WARNING, "Cannot parse ModRM.mod == 3.\n");
goto cannot_emulate;
}
@@ -444,7 +444,8 @@ done:
return length;
cannot_emulate:
- DPRINTK("Cannot emulate %02x at address %lx (eip %lx, mode %d)\n",
+ gdprintk(XENLOG_WARNING,
+ "Cannot emulate %02x at address %lx (eip %lx, mode %d)\n",
b, (unsigned long)_regs.eip, (unsigned long)regs->eip, mode);
return -1;
}
diff --git a/xen/arch/x86/hvm/intercept.c b/xen/arch/x86/hvm/intercept.c
index 288cf2724d..a145659f2d 100644
--- a/xen/arch/x86/hvm/intercept.c
+++ b/xen/arch/x86/hvm/intercept.c
@@ -67,12 +67,12 @@ static inline void hvm_mmio_access(struct vcpu *v,
switch ( p->type ) {
case IOREQ_TYPE_COPY:
{
- if ( !p->pdata_valid ) {
+ if ( !p->data_is_ptr ) {
if ( p->dir == IOREQ_READ )
- p->u.data = read_handler(v, p->addr, p->size);
+ p->data = read_handler(v, p->addr, p->size);
else /* p->dir == IOREQ_WRITE */
- write_handler(v, p->addr, p->size, p->u.data);
- } else { /* !p->pdata_valid */
+ write_handler(v, p->addr, p->size, p->data);
+ } else { /* p->data_is_ptr */
int i, sign = (p->df) ? -1 : 1;
if ( p->dir == IOREQ_READ ) {
@@ -81,7 +81,7 @@ static inline void hvm_mmio_access(struct vcpu *v,
p->addr + (sign * i * p->size),
p->size);
(void)hvm_copy_to_guest_phys(
- (unsigned long)p->u.pdata + (sign * i * p->size),
+ p->data + (sign * i * p->size),
&data,
p->size);
}
@@ -89,7 +89,7 @@ static inline void hvm_mmio_access(struct vcpu *v,
for ( i = 0; i < p->count; i++ ) {
(void)hvm_copy_from_guest_phys(
&data,
- (unsigned long)p->u.pdata + (sign * i * p->size),
+ p->data + (sign * i * p->size),
p->size);
write_handler(v,
p->addr + (sign * i * p->size),
@@ -103,28 +103,37 @@ static inline void hvm_mmio_access(struct vcpu *v,
case IOREQ_TYPE_AND:
tmp1 = read_handler(v, p->addr, p->size);
if ( p->dir == IOREQ_WRITE ) {
- tmp2 = tmp1 & (unsigned long) p->u.data;
+ tmp2 = tmp1 & (unsigned long) p->data;
write_handler(v, p->addr, p->size, tmp2);
}
- p->u.data = tmp1;
+ p->data = tmp1;
+ break;
+
+ case IOREQ_TYPE_ADD:
+ tmp1 = read_handler(v, p->addr, p->size);
+ if (p->dir == IOREQ_WRITE) {
+ tmp2 = tmp1 + (unsigned long) p->data;
+ write_handler(v, p->addr, p->size, tmp2);
+ }
+ p->data = tmp1;
break;
case IOREQ_TYPE_OR:
tmp1 = read_handler(v, p->addr, p->size);
if ( p->dir == IOREQ_WRITE ) {
- tmp2 = tmp1 | (unsigned long) p->u.data;
+ tmp2 = tmp1 | (unsigned long) p->data;
write_handler(v, p->addr, p->size, tmp2);
}
- p->u.data = tmp1;
+ p->data = tmp1;
break;
case IOREQ_TYPE_XOR:
tmp1 = read_handler(v, p->addr, p->size);
if ( p->dir == IOREQ_WRITE ) {
- tmp2 = tmp1 ^ (unsigned long) p->u.data;
+ tmp2 = tmp1 ^ (unsigned long) p->data;
write_handler(v, p->addr, p->size, tmp2);
}
- p->u.data = tmp1;
+ p->data = tmp1;
break;
case IOREQ_TYPE_XCHG:
@@ -133,8 +142,8 @@ static inline void hvm_mmio_access(struct vcpu *v,
* its own local APIC.
*/
tmp1 = read_handler(v, p->addr, p->size);
- write_handler(v, p->addr, p->size, (unsigned long) p->u.data);
- p->u.data = tmp1;
+ write_handler(v, p->addr, p->size, (unsigned long) p->data);
+ p->data = tmp1;
break;
default:
@@ -199,18 +208,17 @@ int hvm_mmio_intercept(ioreq_t *p)
struct vcpu *v = current;
int i;
- /* XXX currently only APIC use intercept */
- if ( !hvm_apic_support(v->domain) )
- return 0;
-
- for ( i = 0; i < HVM_MMIO_HANDLER_NR; i++ ) {
- if ( hvm_mmio_handlers[i]->check_handler(v, p->addr) ) {
+ for ( i = 0; i < HVM_MMIO_HANDLER_NR; i++ )
+ {
+ if ( hvm_mmio_handlers[i]->check_handler(v, p->addr) )
+ {
hvm_mmio_access(v, p,
hvm_mmio_handlers[i]->read_handler,
hvm_mmio_handlers[i]->write_handler);
return 1;
}
}
+
return 0;
}
@@ -238,15 +246,15 @@ int hvm_io_intercept(ioreq_t *p, int type)
return 0;
}
-int register_io_handler(unsigned long addr, unsigned long size,
- intercept_action_t action, int type)
+int register_io_handler(
+ struct domain *d, unsigned long addr, unsigned long size,
+ intercept_action_t action, int type)
{
- struct vcpu *v = current;
- struct hvm_io_handler *handler =
- &(v->domain->arch.hvm_domain.io_handler);
+ struct hvm_io_handler *handler = &d->arch.hvm_domain.io_handler;
int num = handler->num_slot;
- if (num >= MAX_IO_HANDLER) {
+ if ( num >= MAX_IO_HANDLER )
+ {
printk("no extra space, register io interceptor failed!\n");
domain_crash_synchronous();
}
@@ -260,13 +268,6 @@ int register_io_handler(unsigned long addr, unsigned long size,
return 1;
}
-/* Hook function for the HLT instruction emulation wakeup. */
-void hlt_timer_fn(void *data)
-{
- struct vcpu *v = data;
- vcpu_kick(v);
-}
-
static __inline__ void missed_ticks(struct periodic_time *pt)
{
s_time_t missed_ticks;
@@ -289,16 +290,19 @@ static __inline__ void missed_ticks(struct periodic_time *pt)
void pt_timer_fn(void *data)
{
struct vcpu *v = data;
- struct periodic_time *pt = &(v->domain->arch.hvm_domain.pl_time.periodic_tm);
+ struct periodic_time *pt = &v->domain->arch.hvm_domain.pl_time.periodic_tm;
pt->pending_intr_nr++;
pt->scheduled += pt->period;
- /* pick up missed timer tick */
+ /* Pick up missed timer ticks. */
missed_ticks(pt);
- if ( test_bit(_VCPUF_running, &v->vcpu_flags) ) {
+
+ /* No need to run the timer while a VCPU is descheduled. */
+ if ( test_bit(_VCPUF_running, &v->vcpu_flags) )
set_timer(&pt->timer, pt->scheduled);
- }
+
+ vcpu_kick(v);
}
/* pick up missed timer ticks at deactive time */
@@ -325,6 +329,7 @@ struct periodic_time * create_periodic_time(
stop_timer (&pt->timer);
pt->enabled = 0;
}
+ pt->bind_vcpu = 0; /* timer interrupt delivered to BSP by default */
pt->pending_intr_nr = 0;
pt->first_injected = 0;
if (period < 900000) { /* < 0.9 ms */
diff --git a/xen/arch/x86/hvm/io.c b/xen/arch/x86/hvm/io.c
index a1ce8ddf33..0976183bb7 100644
--- a/xen/arch/x86/hvm/io.c
+++ b/xen/arch/x86/hvm/io.c
@@ -365,7 +365,7 @@ static void hvm_pio_assist(struct cpu_user_regs *regs, ioreq_t *p,
unsigned long old_eax;
int sign = p->df ? -1 : 1;
- if ( p->pdata_valid || (pio_opp->flags & OVERLAP) )
+ if ( p->data_is_ptr || (pio_opp->flags & OVERLAP) )
{
if ( pio_opp->flags & REPZ )
regs->ecx -= p->count;
@@ -376,9 +376,9 @@ static void hvm_pio_assist(struct cpu_user_regs *regs, ioreq_t *p,
{
unsigned long addr = pio_opp->addr;
if ( hvm_paging_enabled(current) )
- (void)hvm_copy_to_guest_virt(addr, &p->u.data, p->size);
+ (void)hvm_copy_to_guest_virt(addr, &p->data, p->size);
else
- (void)hvm_copy_to_guest_phys(addr, &p->u.data, p->size);
+ (void)hvm_copy_to_guest_phys(addr, &p->data, p->size);
}
regs->edi += sign * p->count * p->size;
}
@@ -394,18 +394,19 @@ static void hvm_pio_assist(struct cpu_user_regs *regs, ioreq_t *p,
switch ( p->size )
{
case 1:
- regs->eax = (old_eax & 0xffffff00) | (p->u.data & 0xff);
+ regs->eax = (old_eax & 0xffffff00) | (p->data & 0xff);
break;
case 2:
- regs->eax = (old_eax & 0xffff0000) | (p->u.data & 0xffff);
+ regs->eax = (old_eax & 0xffff0000) | (p->data & 0xffff);
break;
case 4:
- regs->eax = (p->u.data & 0xffffffff);
+ regs->eax = (p->data & 0xffffffff);
break;
default:
printk("Error: %s unknown port size\n", __FUNCTION__);
domain_crash_synchronous();
}
+ TRACE_VMEXIT(3, regs->eax);
}
}
@@ -425,7 +426,7 @@ static void hvm_mmio_assist(struct cpu_user_regs *regs, ioreq_t *p,
case INSTR_MOV:
if (dst & REGISTER) {
index = operand_index(dst);
- set_reg_value(size, index, 0, regs, p->u.data);
+ set_reg_value(size, index, 0, regs, p->data);
}
break;
@@ -433,15 +434,15 @@ static void hvm_mmio_assist(struct cpu_user_regs *regs, ioreq_t *p,
if (dst & REGISTER) {
switch (size) {
case BYTE:
- p->u.data &= 0xFFULL;
+ p->data &= 0xFFULL;
break;
case WORD:
- p->u.data &= 0xFFFFULL;
+ p->data &= 0xFFFFULL;
break;
case LONG:
- p->u.data &= 0xFFFFFFFFULL;
+ p->data &= 0xFFFFFFFFULL;
break;
default:
@@ -449,7 +450,7 @@ static void hvm_mmio_assist(struct cpu_user_regs *regs, ioreq_t *p,
domain_crash_synchronous();
}
index = operand_index(dst);
- set_reg_value(operand_size(dst), index, 0, regs, p->u.data);
+ set_reg_value(operand_size(dst), index, 0, regs, p->data);
}
break;
@@ -457,21 +458,21 @@ static void hvm_mmio_assist(struct cpu_user_regs *regs, ioreq_t *p,
if (dst & REGISTER) {
switch (size) {
case BYTE:
- p->u.data &= 0xFFULL;
- if ( p->u.data & 0x80ULL )
- p->u.data |= 0xFFFFFFFFFFFFFF00ULL;
+ p->data &= 0xFFULL;
+ if ( p->data & 0x80ULL )
+ p->data |= 0xFFFFFFFFFFFFFF00ULL;
break;
case WORD:
- p->u.data &= 0xFFFFULL;
- if ( p->u.data & 0x8000ULL )
- p->u.data |= 0xFFFFFFFFFFFF0000ULL;
+ p->data &= 0xFFFFULL;
+ if ( p->data & 0x8000ULL )
+ p->data |= 0xFFFFFFFFFFFF0000ULL;
break;
case LONG:
- p->u.data &= 0xFFFFFFFFULL;
- if ( p->u.data & 0x80000000ULL )
- p->u.data |= 0xFFFFFFFF00000000ULL;
+ p->data &= 0xFFFFFFFFULL;
+ if ( p->data & 0x80000000ULL )
+ p->data |= 0xFFFFFFFF00000000ULL;
break;
default:
@@ -479,7 +480,7 @@ static void hvm_mmio_assist(struct cpu_user_regs *regs, ioreq_t *p,
domain_crash_synchronous();
}
index = operand_index(dst);
- set_reg_value(operand_size(dst), index, 0, regs, p->u.data);
+ set_reg_value(operand_size(dst), index, 0, regs, p->data);
}
break;
@@ -493,9 +494,9 @@ static void hvm_mmio_assist(struct cpu_user_regs *regs, ioreq_t *p,
unsigned long addr = mmio_opp->addr;
if (hvm_paging_enabled(current))
- (void)hvm_copy_to_guest_virt(addr, &p->u.data, p->size);
+ (void)hvm_copy_to_guest_virt(addr, &p->data, p->size);
else
- (void)hvm_copy_to_guest_phys(addr, &p->u.data, p->size);
+ (void)hvm_copy_to_guest_phys(addr, &p->data, p->size);
}
regs->esi += sign * p->count * p->size;
@@ -521,14 +522,29 @@ static void hvm_mmio_assist(struct cpu_user_regs *regs, ioreq_t *p,
if (src & REGISTER) {
index = operand_index(src);
value = get_reg_value(size, index, 0, regs);
- diff = (unsigned long) p->u.data & value;
+ diff = (unsigned long) p->data & value;
} else if (src & IMMEDIATE) {
value = mmio_opp->immediate;
- diff = (unsigned long) p->u.data & value;
+ diff = (unsigned long) p->data & value;
} else if (src & MEMORY) {
index = operand_index(dst);
value = get_reg_value(size, index, 0, regs);
- diff = (unsigned long) p->u.data & value;
+ diff = (unsigned long) p->data & value;
+ set_reg_value(size, index, 0, regs, diff);
+ }
+
+ case INSTR_ADD:
+ if (src & REGISTER) {
+ index = operand_index(src);
+ value = get_reg_value(size, index, 0, regs);
+ diff = (unsigned long) p->data + value;
+ } else if (src & IMMEDIATE) {
+ value = mmio_opp->immediate;
+ diff = (unsigned long) p->data + value;
+ } else if (src & MEMORY) {
+ index = operand_index(dst);
+ value = get_reg_value(size, index, 0, regs);
+ diff = (unsigned long) p->data + value;
set_reg_value(size, index, 0, regs, diff);
}
@@ -548,14 +564,14 @@ static void hvm_mmio_assist(struct cpu_user_regs *regs, ioreq_t *p,
if (src & REGISTER) {
index = operand_index(src);
value = get_reg_value(size, index, 0, regs);
- diff = (unsigned long) p->u.data | value;
+ diff = (unsigned long) p->data | value;
} else if (src & IMMEDIATE) {
value = mmio_opp->immediate;
- diff = (unsigned long) p->u.data | value;
+ diff = (unsigned long) p->data | value;
} else if (src & MEMORY) {
index = operand_index(dst);
value = get_reg_value(size, index, 0, regs);
- diff = (unsigned long) p->u.data | value;
+ diff = (unsigned long) p->data | value;
set_reg_value(size, index, 0, regs, diff);
}
@@ -575,14 +591,14 @@ static void hvm_mmio_assist(struct cpu_user_regs *regs, ioreq_t *p,
if (src & REGISTER) {
index = operand_index(src);
value = get_reg_value(size, index, 0, regs);
- diff = (unsigned long) p->u.data ^ value;
+ diff = (unsigned long) p->data ^ value;
} else if (src & IMMEDIATE) {
value = mmio_opp->immediate;
- diff = (unsigned long) p->u.data ^ value;
+ diff = (unsigned long) p->data ^ value;
} else if (src & MEMORY) {
index = operand_index(dst);
value = get_reg_value(size, index, 0, regs);
- diff = (unsigned long) p->u.data ^ value;
+ diff = (unsigned long) p->data ^ value;
set_reg_value(size, index, 0, regs, diff);
}
@@ -603,14 +619,14 @@ static void hvm_mmio_assist(struct cpu_user_regs *regs, ioreq_t *p,
if (src & REGISTER) {
index = operand_index(src);
value = get_reg_value(size, index, 0, regs);
- diff = (unsigned long) p->u.data - value;
+ diff = (unsigned long) p->data - value;
} else if (src & IMMEDIATE) {
value = mmio_opp->immediate;
- diff = (unsigned long) p->u.data - value;
+ diff = (unsigned long) p->data - value;
} else if (src & MEMORY) {
index = operand_index(dst);
value = get_reg_value(size, index, 0, regs);
- diff = value - (unsigned long) p->u.data;
+ diff = value - (unsigned long) p->data;
if ( mmio_opp->instr == INSTR_SUB )
set_reg_value(size, index, 0, regs, diff);
}
@@ -621,9 +637,9 @@ static void hvm_mmio_assist(struct cpu_user_regs *regs, ioreq_t *p,
*/
regs->eflags &= ~(X86_EFLAGS_CF|X86_EFLAGS_PF|X86_EFLAGS_AF|
X86_EFLAGS_ZF|X86_EFLAGS_SF|X86_EFLAGS_OF);
- set_eflags_CF(size, value, (unsigned long) p->u.data, regs);
- set_eflags_OF(size, diff, value, (unsigned long) p->u.data, regs);
- set_eflags_AF(size, diff, value, (unsigned long) p->u.data, regs);
+ set_eflags_CF(size, value, (unsigned long) p->data, regs);
+ set_eflags_OF(size, diff, value, (unsigned long) p->data, regs);
+ set_eflags_AF(size, diff, value, (unsigned long) p->data, regs);
set_eflags_ZF(size, diff, regs);
set_eflags_SF(size, diff, regs);
set_eflags_PF(size, diff, regs);
@@ -639,7 +655,7 @@ static void hvm_mmio_assist(struct cpu_user_regs *regs, ioreq_t *p,
index = operand_index(dst);
value = get_reg_value(size, index, 0, regs);
}
- diff = (unsigned long) p->u.data & value;
+ diff = (unsigned long) p->data & value;
/*
* Sets the SF, ZF, and PF status flags. CF and OF are set to 0
@@ -659,7 +675,7 @@ static void hvm_mmio_assist(struct cpu_user_regs *regs, ioreq_t *p,
}
else if ( src & IMMEDIATE )
value = mmio_opp->immediate;
- if (p->u.data & (1 << (value & ((1 << 5) - 1))))
+ if (p->data & (1 << (value & ((1 << 5) - 1))))
regs->eflags |= X86_EFLAGS_CF;
else
regs->eflags &= ~X86_EFLAGS_CF;
@@ -669,10 +685,10 @@ static void hvm_mmio_assist(struct cpu_user_regs *regs, ioreq_t *p,
case INSTR_XCHG:
if (src & REGISTER) {
index = operand_index(src);
- set_reg_value(size, index, 0, regs, p->u.data);
+ set_reg_value(size, index, 0, regs, p->data);
} else {
index = operand_index(dst);
- set_reg_value(size, index, 0, regs, p->u.data);
+ set_reg_value(size, index, 0, regs, p->data);
}
break;
}
@@ -683,7 +699,8 @@ void hvm_interrupt_post(struct vcpu *v, int vector, int type)
struct periodic_time *pt =
&(v->domain->arch.hvm_domain.pl_time.periodic_tm);
- if ( pt->enabled && is_periodic_irq(v, vector, type) ) {
+ if ( pt->enabled && v->vcpu_id == pt->bind_vcpu
+ && is_periodic_irq(v, vector, type) ) {
if ( !pt->first_injected ) {
pt->pending_intr_nr = 0;
pt->last_plt_gtime = hvm_get_guest_time(v);
@@ -719,27 +736,27 @@ void hvm_io_assist(struct vcpu *v)
io_opp = &v->arch.hvm_vcpu.io_op;
regs = &io_opp->io_context;
+ vio = get_vio(v->domain, v->vcpu_id);
- vio = get_vio(v->domain, v->vcpu_id);
-
- if ( vio == 0 ) {
- printk("bad shared page: %lx\n", (unsigned long)vio);
+ p = &vio->vp_ioreq;
+ if ( p->state != STATE_IORESP_READY )
+ {
+ gdprintk(XENLOG_ERR, "Unexpected HVM iorequest state %d.\n", p->state);
domain_crash_synchronous();
}
- p = &vio->vp_ioreq;
+ rmb(); /* see IORESP_READY /then/ read contents of ioreq */
- if ( p->state == STATE_IORESP_READY ) {
- p->state = STATE_INVALID;
- if ( p->type == IOREQ_TYPE_PIO )
- hvm_pio_assist(regs, p, io_opp);
- else
- hvm_mmio_assist(regs, p, io_opp);
+ p->state = STATE_IOREQ_NONE;
- /* Copy register changes back into current guest state. */
- hvm_load_cpu_guest_regs(v, regs);
- memcpy(guest_cpu_user_regs(), regs, HVM_CONTEXT_STACK_BYTES);
- }
+ if ( p->type == IOREQ_TYPE_PIO )
+ hvm_pio_assist(regs, p, io_opp);
+ else
+ hvm_mmio_assist(regs, p, io_opp);
+
+ /* Copy register changes back into current guest state. */
+ hvm_load_cpu_guest_regs(v, regs);
+ memcpy(guest_cpu_user_regs(), regs, HVM_CONTEXT_STACK_BYTES);
}
/*
diff --git a/xen/arch/x86/hvm/platform.c b/xen/arch/x86/hvm/platform.c
index 7f2c32641d..0afae0e4d6 100644
--- a/xen/arch/x86/hvm/platform.c
+++ b/xen/arch/x86/hvm/platform.c
@@ -370,6 +370,13 @@ static int hvm_decode(int realmode, unsigned char *opcode,
/* the operands order in comments conforms to AT&T convention */
switch ( *opcode ) {
+
+ case 0x00: /* add r8, m8 */
+ mmio_op->instr = INSTR_ADD;
+ *op_size = BYTE;
+ GET_OP_SIZE_FOR_BYTE(size_reg);
+ return reg_mem(size_reg, opcode, mmio_op, rex);
+
case 0x0A: /* or m8, r8 */
mmio_op->instr = INSTR_OR;
*op_size = BYTE;
@@ -720,21 +727,26 @@ static void hvm_send_assist_req(struct vcpu *v)
ioreq_t *p;
p = &get_vio(v->domain, v->vcpu_id)->vp_ioreq;
- if ( unlikely(p->state != STATE_INVALID) ) {
- /* This indicates a bug in the device model. Crash the
- domain. */
- printk("Device model set bad IO state %d.\n", p->state);
+ if ( unlikely(p->state != STATE_IOREQ_NONE) )
+ {
+ /* This indicates a bug in the device model. Crash the domain. */
+ gdprintk(XENLOG_ERR, "Device model set bad IO state %d.\n", p->state);
domain_crash(v->domain);
return;
}
prepare_wait_on_xen_event_channel(v->arch.hvm_vcpu.xen_port);
+
+ /*
+ * Following happens /after/ blocking and setting up ioreq contents.
+ * prepare_wait_on_xen_event_channel() is an implicit barrier.
+ */
p->state = STATE_IOREQ_READY;
notify_via_xen_event_channel(v->arch.hvm_vcpu.xen_port);
}
void send_pio_req(unsigned long port, unsigned long count, int size,
- long value, int dir, int df, int pvalid)
+ long value, int dir, int df, int value_is_ptr)
{
struct vcpu *v = current;
vcpu_iodata_t *vio;
@@ -742,8 +754,8 @@ void send_pio_req(unsigned long port, unsigned long count, int size,
if ( size == 0 || count == 0 ) {
printk("null pio request? port %lx, count %lx, "
- "size %d, value %lx, dir %d, pvalid %d.\n",
- port, count, size, value, dir, pvalid);
+ "size %d, value %lx, dir %d, value_is_ptr %d.\n",
+ port, count, size, value, dir, value_is_ptr);
}
vio = get_vio(v->domain, v->vcpu_id);
@@ -753,12 +765,12 @@ void send_pio_req(unsigned long port, unsigned long count, int size,
}
p = &vio->vp_ioreq;
- if ( p->state != STATE_INVALID )
+ if ( p->state != STATE_IOREQ_NONE )
printk("WARNING: send pio with something already pending (%d)?\n",
p->state);
p->dir = dir;
- p->pdata_valid = pvalid;
+ p->data_is_ptr = value_is_ptr;
p->type = IOREQ_TYPE_PIO;
p->size = size;
@@ -768,16 +780,18 @@ void send_pio_req(unsigned long port, unsigned long count, int size,
p->io_count++;
- if ( pvalid ) /* get physical address of data */
+ if ( value_is_ptr ) /* get physical address of data */
{
if ( hvm_paging_enabled(current) )
- p->u.pdata = (void *)shadow_gva_to_gpa(current, value);
+ p->data = shadow_gva_to_gpa(current, value);
else
- p->u.pdata = (void *)value; /* guest VA == guest PA */
- } else if ( dir == IOREQ_WRITE )
- p->u.data = value;
+ p->data = value; /* guest VA == guest PA */
+ }
+ else if ( dir == IOREQ_WRITE )
+ p->data = value;
- if ( hvm_portio_intercept(p) ) {
+ if ( hvm_portio_intercept(p) )
+ {
p->state = STATE_IORESP_READY;
hvm_io_assist(v);
return;
@@ -788,7 +802,7 @@ void send_pio_req(unsigned long port, unsigned long count, int size,
static void send_mmio_req(unsigned char type, unsigned long gpa,
unsigned long count, int size, long value,
- int dir, int df, int pvalid)
+ int dir, int df, int value_is_ptr)
{
struct vcpu *v = current;
vcpu_iodata_t *vio;
@@ -796,8 +810,8 @@ static void send_mmio_req(unsigned char type, unsigned long gpa,
if ( size == 0 || count == 0 ) {
printk("null mmio request? type %d, gpa %lx, "
- "count %lx, size %d, value %lx, dir %d, pvalid %d.\n",
- type, gpa, count, size, value, dir, pvalid);
+ "count %lx, size %d, value %lx, dir %d, value_is_ptr %d.\n",
+ type, gpa, count, size, value, dir, value_is_ptr);
}
vio = get_vio(v->domain, v->vcpu_id);
@@ -808,11 +822,11 @@ static void send_mmio_req(unsigned char type, unsigned long gpa,
p = &vio->vp_ioreq;
- if ( p->state != STATE_INVALID )
+ if ( p->state != STATE_IOREQ_NONE )
printk("WARNING: send mmio with something already pending (%d)?\n",
p->state);
p->dir = dir;
- p->pdata_valid = pvalid;
+ p->data_is_ptr = value_is_ptr;
p->type = type;
p->size = size;
@@ -822,15 +836,18 @@ static void send_mmio_req(unsigned char type, unsigned long gpa,
p->io_count++;
- if (pvalid) {
- if (hvm_paging_enabled(v))
- p->u.data = shadow_gva_to_gpa(v, value);
+ if ( value_is_ptr )
+ {
+ if ( hvm_paging_enabled(v) )
+ p->data = shadow_gva_to_gpa(v, value);
else
- p->u.pdata = (void *) value; /* guest VA == guest PA */
- } else
- p->u.data = value;
+ p->data = value; /* guest VA == guest PA */
+ }
+ else
+ p->data = value;
- if ( hvm_mmio_intercept(p) || hvm_buffered_io_intercept(p) ) {
+ if ( hvm_mmio_intercept(p) || hvm_buffered_io_intercept(p) )
+ {
p->state = STATE_IORESP_READY;
hvm_io_assist(v);
return;
@@ -956,7 +973,8 @@ void handle_mmio(unsigned long gpa)
}
if ( addr & (size - 1) )
- DPRINTK("Unaligned ioport access: %lx, %d\n", addr, size);
+ gdprintk(XENLOG_WARNING,
+ "Unaligned ioport access: %lx, %d\n", addr, size);
/*
* In case of a movs spanning multiple pages, we break the accesses
@@ -971,7 +989,8 @@ void handle_mmio(unsigned long gpa)
if ( (addr & PAGE_MASK) != ((addr + size - 1) & PAGE_MASK) ) {
unsigned long value = 0;
- DPRINTK("Single io request in a movs crossing page boundary.\n");
+ gdprintk(XENLOG_WARNING,
+ "Single io request in a movs crossing page boundary.\n");
mmio_op->flags |= OVERLAP;
if ( dir == IOREQ_WRITE ) {
@@ -1038,6 +1057,10 @@ void handle_mmio(unsigned long gpa)
mmio_operands(IOREQ_TYPE_AND, gpa, mmio_op, op_size);
break;
+ case INSTR_ADD:
+ mmio_operands(IOREQ_TYPE_ADD, gpa, mmio_op, op_size);
+ break;
+
case INSTR_XOR:
mmio_operands(IOREQ_TYPE_XOR, gpa, mmio_op, op_size);
break;
diff --git a/xen/arch/x86/hvm/pmtimer.c b/xen/arch/x86/hvm/pmtimer.c
index e0c93536ea..b435fbdf9f 100644
--- a/xen/arch/x86/hvm/pmtimer.c
+++ b/xen/arch/x86/hvm/pmtimer.c
@@ -20,7 +20,7 @@ static int handle_pmt_io(ioreq_t *p)
uint64_t curr_gtime;
if (p->size != 4 ||
- p->pdata_valid ||
+ p->data_is_ptr ||
p->type != IOREQ_TYPE_PIO){
printk("HVM_PMT: wrong PM timer IO\n");
return 1;
@@ -32,7 +32,7 @@ static int handle_pmt_io(ioreq_t *p)
} else if (p->dir == 1) { /* read */
curr_gtime = hvm_get_guest_time(s->vcpu);
s->pm1_timer += ((curr_gtime - s->last_gtime) * s->scale) >> 32;
- p->u.data = s->pm1_timer;
+ p->data = s->pm1_timer;
s->last_gtime = curr_gtime;
return 1;
}
@@ -52,7 +52,7 @@ void pmtimer_init(struct vcpu *v, int base)
/* ACPI supports a 32-bit power management timer */
set_timer(&s->timer, NOW() + (1000000000ULL << 31) / FREQUENCE_PMTIMER);
- register_portio_handler(base, 4, handle_pmt_io);
+ register_portio_handler(v->domain, base, 4, handle_pmt_io);
}
void pmtimer_deinit(struct domain *d)
diff --git a/xen/arch/x86/hvm/rtc.c b/xen/arch/x86/hvm/rtc.c
index 0f5e11986e..a2ae171d85 100644
--- a/xen/arch/x86/hvm/rtc.c
+++ b/xen/arch/x86/hvm/rtc.c
@@ -4,10 +4,10 @@
* Copyright (c) 2003-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
+ * 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
@@ -17,9 +17,9 @@
* 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.
+ * 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/mc146818rtc.h>
@@ -263,8 +263,8 @@ static void rtc_update_second(void *opaque)
static void rtc_update_second2(void *opaque)
{
RTCState *s = opaque;
- struct hvm_domain *plat=&s->vcpu->domain->arch.hvm_domain;
- struct hvm_virpic *pic= &plat->vpic;
+ struct hvm_domain *plat = &s->vcpu->domain->arch.hvm_domain;
+ struct vpic *pic = &plat->vpic;
if (!(s->cmos_data[RTC_REG_B] & RTC_SET)) {
rtc_copy_date(s);
@@ -302,8 +302,8 @@ static void rtc_update_second2(void *opaque)
static uint32_t rtc_ioport_read(void *opaque, uint32_t addr)
{
RTCState *s = opaque;
- struct hvm_domain *plat=&s->vcpu->domain->arch.hvm_domain;
- struct hvm_virpic *pic= &plat->vpic;
+ struct hvm_domain *plat = &s->vcpu->domain->arch.hvm_domain;
+ struct vpic *pic = &plat->vpic;
int ret;
if ((addr & 1) == 0) {
@@ -345,17 +345,17 @@ static int handle_rtc_io(ioreq_t *p)
struct RTCState *vrtc = &v->domain->arch.hvm_domain.pl_time.vrtc;
if (p->size != 1 ||
- p->pdata_valid ||
+ p->data_is_ptr ||
p->type != IOREQ_TYPE_PIO){
printk("HVM_RTC: wrong RTC IO!\n");
return 1;
}
if (p->dir == 0) { /* write */
- if (rtc_ioport_write(vrtc, p->addr, p->u.data & 0xFF))
+ if (rtc_ioport_write(vrtc, p->addr, p->data & 0xFF))
return 1;
} else if (p->dir == 1 && vrtc->cmos_index < RTC_SIZE) { /* read */
- p->u.data = rtc_ioport_read(vrtc, p->addr);
+ p->data = rtc_ioport_read(vrtc, p->addr);
return 1;
}
return 0;
@@ -381,7 +381,7 @@ void rtc_init(struct vcpu *v, int base, int irq)
s->next_second_time = NOW() + 1000000000ULL;
set_timer(&s->second_timer2, s->next_second_time);
- register_portio_handler(base, 2, handle_rtc_io);
+ register_portio_handler(v->domain, base, 2, handle_rtc_io);
}
void rtc_deinit(struct domain *d)
diff --git a/xen/arch/x86/hvm/svm/intr.c b/xen/arch/x86/hvm/svm/intr.c
index 0b1710b767..29d825cc28 100644
--- a/xen/arch/x86/hvm/svm/intr.c
+++ b/xen/arch/x86/hvm/svm/intr.c
@@ -46,12 +46,8 @@
static inline int svm_inject_extint(struct vcpu *v, int trap)
{
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
- vintr_t intr;
+ vintr_t intr = vmcb->vintr;
- ASSERT(vmcb);
-
- /* Save all fields */
- intr = vmcb->vintr;
/* Update only relevant fields */
intr.fields.irq = 1;
intr.fields.intr_masking = 1;
@@ -69,17 +65,16 @@ asmlinkage void svm_intr_assist(void)
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
struct hvm_domain *plat=&v->domain->arch.hvm_domain;
struct periodic_time *pt = &plat->pl_time.periodic_tm;
- struct hvm_virpic *pic= &plat->vpic;
+ struct vpic *pic= &plat->vpic;
int callback_irq;
int intr_type = APIC_DM_EXTINT;
int intr_vector = -1;
int re_injecting = 0;
- ASSERT(vmcb);
-
/* Check if an Injection is active */
/* Previous Interrupt delivery caused this Intercept? */
- if (vmcb->exitintinfo.fields.v && (vmcb->exitintinfo.fields.type == 0)) {
+ if ( vmcb->exitintinfo.fields.v && (vmcb->exitintinfo.fields.type == 0) )
+ {
v->arch.hvm_svm.saved_irq_vector = vmcb->exitintinfo.fields.vector;
vmcb->exitintinfo.bytes = 0;
re_injecting = 1;
@@ -88,70 +83,71 @@ asmlinkage void svm_intr_assist(void)
/*
* If event requires injecting then do not inject int.
*/
- if (unlikely(v->arch.hvm_svm.inject_event)) {
+ if ( unlikely(v->arch.hvm_svm.inject_event) )
+ {
v->arch.hvm_svm.inject_event = 0;
return;
}
/*
- * create a 'fake' virtual interrupt on to intercept as soon
- * as the guest _can_ take interrupts
+ * Create a 'fake' virtual interrupt on to intercept as soon
+ * as the guest _can_ take interrupts.
*/
- if (irq_masked(vmcb->rflags) || vmcb->interrupt_shadow) {
+ if ( irq_masked(vmcb->rflags) || vmcb->interrupt_shadow )
+ {
vmcb->general1_intercepts |= GENERAL1_INTERCEPT_VINTR;
svm_inject_extint(v, 0x0); /* actual vector doesn't really matter */
return;
}
/* Previous interrupt still pending? */
- if (vmcb->vintr.fields.irq) {
-// printk("Re-injecting IRQ from Vintr\n");
+ if ( vmcb->vintr.fields.irq )
+ {
intr_vector = vmcb->vintr.fields.vector;
vmcb->vintr.bytes = 0;
re_injecting = 1;
}
/* Pending IRQ saved at last VMExit? */
- else if ( v->arch.hvm_svm.saved_irq_vector >= 0) {
-// printk("Re-Injecting saved IRQ\n");
+ else if ( v->arch.hvm_svm.saved_irq_vector >= 0 )
+ {
intr_vector = v->arch.hvm_svm.saved_irq_vector;
v->arch.hvm_svm.saved_irq_vector = -1;
re_injecting = 1;
}
/* Now let's check for newer interrrupts */
- else {
-
- if ( v->vcpu_id == 0 )
- hvm_pic_assist(v);
-
-
- if ( (v->vcpu_id == 0) && pt->enabled && pt->pending_intr_nr ) {
- pic_set_irq(pic, pt->irq, 0);
- pic_set_irq(pic, pt->irq, 1);
- }
-
- if (v->vcpu_id == 0) {
- callback_irq =
- v->domain->arch.hvm_domain.params[HVM_PARAM_CALLBACK_IRQ];
- if ( callback_irq != 0)
- pic_set_xen_irq(pic, callback_irq, local_events_need_delivery());
- }
+ else
+ {
+ if ( (v->vcpu_id == 0) && pt->enabled && pt->pending_intr_nr )
+ {
+ pic_set_irq(pic, pt->irq, 0);
+ pic_set_irq(pic, pt->irq, 1);
+ }
- if ( cpu_has_pending_irq(v) )
- intr_vector = cpu_get_interrupt(v, &intr_type);
+ if ( v->vcpu_id == 0 )
+ {
+ callback_irq =
+ v->domain->arch.hvm_domain.params[HVM_PARAM_CALLBACK_IRQ];
+ if ( callback_irq != 0 )
+ pic_set_xen_irq(pic, callback_irq,
+ local_events_need_delivery());
+ }
+ if ( cpu_has_pending_irq(v) )
+ intr_vector = cpu_get_interrupt(v, &intr_type);
}
/* have we got an interrupt to inject? */
- if (intr_vector >= 0) {
- switch (intr_type) {
+ if ( intr_vector >= 0 )
+ {
+ switch ( intr_type )
+ {
case APIC_DM_EXTINT:
case APIC_DM_FIXED:
case APIC_DM_LOWEST:
/* Re-injecting a PIT interruptt? */
- if (re_injecting && pt->enabled &&
- is_periodic_irq(v, intr_vector, intr_type)) {
- ++pt->pending_intr_nr;
- }
+ if ( re_injecting && pt->enabled &&
+ is_periodic_irq(v, intr_vector, intr_type) )
+ ++pt->pending_intr_nr;
/* let's inject this interrupt */
TRACE_3D(TRC_VMX_INTR, v->domain->domain_id, intr_vector, 0);
svm_inject_extint(v, intr_vector);
diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c
index 88c0802425..7b8bf46258 100644
--- a/xen/arch/x86/hvm/svm/svm.c
+++ b/xen/arch/x86/hvm/svm/svm.c
@@ -56,13 +56,11 @@
extern int inst_copy_from_guest(unsigned char *buf, unsigned long guest_eip,
int inst_len);
-extern uint32_t vlapic_update_ppr(struct vlapic *vlapic);
extern asmlinkage void do_IRQ(struct cpu_user_regs *);
extern void svm_dump_inst(unsigned long eip);
extern int svm_dbg_on;
void svm_dump_regs(const char *from, struct cpu_user_regs *regs);
-static void svm_relinquish_guest_resources(struct domain *d);
static int svm_do_vmmcall_reset_to_realmode(struct vcpu *v,
struct cpu_user_regs *regs);
@@ -215,11 +213,8 @@ static void stop_svm(void)
free_vmcb(root_vmcb[cpu]);
root_vmcb[cpu] = NULL;
root_vmcb_pa[cpu] = 0;
-
- printk("AMD SVM Extension is disabled.\n");
}
-
static void svm_store_cpu_guest_regs(
struct vcpu *v, struct cpu_user_regs *regs, unsigned long *crs)
{
@@ -269,6 +264,11 @@ static int svm_pae_enabled(struct vcpu *v)
return (cr4 & X86_CR4_PAE);
}
+static int svm_long_mode_enabled(struct vcpu *v)
+{
+ return test_bit(SVM_CPU_STATE_LMA_ENABLED, &v->arch.hvm_svm.cpu_state);
+}
+
#define IS_CANO_ADDRESS(add) 1
static inline int long_mode_do_msr_read(struct cpu_user_regs *regs)
@@ -374,7 +374,7 @@ static inline int long_mode_do_msr_write(struct cpu_user_regs *regs)
case MSR_FS_BASE:
case MSR_GS_BASE:
- if (!(SVM_LONG_GUEST(vc)))
+ if ( !svm_long_mode_enabled(vc) )
domain_crash_synchronous();
if (!IS_CANO_ADDRESS(msr_content))
@@ -424,17 +424,21 @@ static inline int long_mode_do_msr_write(struct cpu_user_regs *regs)
static inline void svm_save_dr(struct vcpu *v)
{
- if (v->arch.hvm_vcpu.flag_dr_dirty)
- {
- /* clear the DR dirty flag and re-enable intercepts for DR accesses */
- v->arch.hvm_vcpu.flag_dr_dirty = 0;
- v->arch.hvm_svm.vmcb->dr_intercepts = DR_INTERCEPT_ALL_WRITES;
-
- savedebug(&v->arch.guest_context, 0);
- savedebug(&v->arch.guest_context, 1);
- savedebug(&v->arch.guest_context, 2);
- savedebug(&v->arch.guest_context, 3);
- }
+ struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
+
+ if ( !v->arch.hvm_vcpu.flag_dr_dirty )
+ return;
+
+ /* Clear the DR dirty flag and re-enable intercepts for DR accesses. */
+ v->arch.hvm_vcpu.flag_dr_dirty = 0;
+ v->arch.hvm_svm.vmcb->dr_intercepts = DR_INTERCEPT_ALL_WRITES;
+
+ savedebug(&v->arch.guest_context, 0);
+ savedebug(&v->arch.guest_context, 1);
+ savedebug(&v->arch.guest_context, 2);
+ savedebug(&v->arch.guest_context, 3);
+ v->arch.guest_context.debugreg[6] = vmcb->dr6;
+ v->arch.guest_context.debugreg[7] = vmcb->dr7;
}
@@ -444,17 +448,13 @@ static inline void __restore_debug_registers(struct vcpu *v)
loaddebug(&v->arch.guest_context, 1);
loaddebug(&v->arch.guest_context, 2);
loaddebug(&v->arch.guest_context, 3);
+ /* DR6 and DR7 are loaded from the VMCB. */
}
static inline void svm_restore_dr(struct vcpu *v)
{
- struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
-
- if (!vmcb)
- return;
-
- if (unlikely(vmcb->dr7 & 0xFF))
+ if ( unlikely(v->arch.guest_context.debugreg[7] & 0xFF) )
__restore_debug_registers(v);
}
@@ -531,30 +531,10 @@ static void svm_set_tsc_offset(struct vcpu *v, u64 offset)
}
-/* SVM-specific intitialization code for VCPU application processors */
-static void svm_init_ap_context(struct vcpu_guest_context *ctxt,
- int vcpuid, int trampoline_vector)
+static void svm_init_ap_context(
+ struct vcpu_guest_context *ctxt, int vcpuid, int trampoline_vector)
{
- int i;
- struct vcpu *v, *bsp = current;
- struct domain *d = bsp->domain;
- cpu_user_regs_t *regs;;
-
-
- if ((v = d->vcpu[vcpuid]) == NULL)
- {
- printk("vcpuid %d is invalid! good-bye.\n", vcpuid);
- domain_crash_synchronous();
- }
- regs = &v->arch.guest_context.user_regs;
-
memset(ctxt, 0, sizeof(*ctxt));
- for (i = 0; i < 256; ++i)
- {
- ctxt->trap_ctxt[i].vector = i;
- ctxt->trap_ctxt[i].cs = FLAT_KERNEL_CS;
- }
-
/*
* We execute the trampoline code in real mode. The trampoline vector
@@ -563,7 +543,6 @@ static void svm_init_ap_context(struct vcpu_guest_context *ctxt,
*/
ctxt->user_regs.eip = 0x0;
ctxt->user_regs.cs = (trampoline_vector << 8);
- ctxt->flags = VGCF_HVM_GUEST;
}
static void svm_init_hypercall_page(struct domain *d, void *hypercall_page)
@@ -693,7 +672,7 @@ static void svm_load_cpu_user_regs(struct vcpu *v, struct cpu_user_regs *regs)
vmcb->rax = regs->eax;
vmcb->ss.sel = regs->ss;
vmcb->rsp = regs->esp;
- vmcb->rflags = regs->eflags;
+ vmcb->rflags = regs->eflags | 2UL;
vmcb->cs.sel = regs->cs;
vmcb->rip = regs->eip;
if (regs->eflags & EF_TF)
@@ -708,40 +687,13 @@ static void svm_load_cpu_guest_regs(
svm_load_cpu_user_regs(v, regs);
}
-int svm_long_mode_enabled(struct vcpu *v)
-{
- return SVM_LONG_GUEST(v);
-}
-
-
-
static void arch_svm_do_launch(struct vcpu *v)
{
- cpu_user_regs_t *regs = &current->arch.guest_context.user_regs;
- int error;
-
-#if 0
- if (svm_dbg_on)
- printk("Do launch\n");
-#endif
- error = construct_vmcb(&v->arch.hvm_svm, regs);
- if ( error < 0 )
- {
- if (v->vcpu_id == 0) {
- printk("Failed to construct a new VMCB for BSP.\n");
- } else {
- printk("Failed to construct a new VMCB for AP %d\n", v->vcpu_id);
- }
- domain_crash_synchronous();
- }
-
svm_do_launch(v);
-#if 0
- if (svm_dbg_on)
- svm_dump_host_regs(__func__);
-#endif
- if (v->vcpu_id != 0)
+
+ if ( v->vcpu_id != 0 )
{
+ cpu_user_regs_t *regs = &current->arch.guest_context.user_regs;
u16 cs_sel = regs->cs;
/*
* This is the launch of an AP; set state so that we begin executing
@@ -760,10 +712,13 @@ static void arch_svm_do_launch(struct vcpu *v)
static void svm_freeze_time(struct vcpu *v)
{
struct periodic_time *pt=&v->domain->arch.hvm_domain.pl_time.periodic_tm;
-
- if ( pt->enabled && pt->first_injected && !v->arch.hvm_vcpu.guest_time ) {
+
+ if ( pt->enabled && pt->first_injected
+ && (v->vcpu_id == pt->bind_vcpu)
+ && !v->arch.hvm_vcpu.guest_time ) {
v->arch.hvm_vcpu.guest_time = hvm_get_guest_time(v);
- stop_timer(&(pt->timer));
+ if ( test_bit(_VCPUF_blocked, &v->vcpu_flags) )
+ stop_timer(&pt->timer);
}
}
@@ -790,41 +745,32 @@ static void svm_ctxt_switch_to(struct vcpu *v)
svm_restore_dr(v);
}
-
-static void svm_final_setup_guest(struct vcpu *v)
+static int svm_vcpu_initialise(struct vcpu *v)
{
- struct domain *d = v->domain;
+ int rc;
v->arch.schedule_tail = arch_svm_do_launch;
v->arch.ctxt_switch_from = svm_ctxt_switch_from;
v->arch.ctxt_switch_to = svm_ctxt_switch_to;
- if ( v != d->vcpu[0] )
- return;
+ v->arch.hvm_svm.saved_irq_vector = -1;
- if ( !shadow_mode_external(d) )
+ if ( (rc = svm_create_vmcb(v)) != 0 )
{
- DPRINTK("Can't init HVM for dom %u vcpu %u: "
- "not in shadow external mode\n", d->domain_id, v->vcpu_id);
- domain_crash(d);
+ dprintk(XENLOG_WARNING,
+ "Failed to create VMCB for vcpu %d: err=%d.\n",
+ v->vcpu_id, rc);
+ return rc;
}
- /*
- * Required to do this once per domain
- * TODO: add a seperate function to do these.
- */
- memset(&d->shared_info->evtchn_mask[0], 0xff,
- sizeof(d->shared_info->evtchn_mask));
+ return 0;
}
-
-static int svm_initialize_guest_resources(struct vcpu *v)
+static void svm_vcpu_destroy(struct vcpu *v)
{
- svm_final_setup_guest(v);
- return 1;
+ svm_destroy_vmcb(v);
}
-
int start_svm(void)
{
u32 eax, ecx, edx;
@@ -872,8 +818,8 @@ int start_svm(void)
/* Setup HVM interfaces */
hvm_funcs.disable = stop_svm;
- hvm_funcs.initialize_guest_resources = svm_initialize_guest_resources;
- hvm_funcs.relinquish_guest_resources = svm_relinquish_guest_resources;
+ hvm_funcs.vcpu_initialise = svm_vcpu_initialise;
+ hvm_funcs.vcpu_destroy = svm_vcpu_destroy;
hvm_funcs.store_cpu_guest_regs = svm_store_cpu_guest_regs;
hvm_funcs.load_cpu_guest_regs = svm_load_cpu_guest_regs;
@@ -899,40 +845,6 @@ int start_svm(void)
}
-static void svm_relinquish_guest_resources(struct domain *d)
-{
- struct vcpu *v;
-
- for_each_vcpu ( d, v )
- {
- if ( !test_bit(_VCPUF_initialised, &v->vcpu_flags) )
- continue;
-
- destroy_vmcb(&v->arch.hvm_svm);
- kill_timer(&v->arch.hvm_vcpu.hlt_timer);
- if ( VLAPIC(v) != NULL )
- {
- kill_timer(&VLAPIC(v)->vlapic_timer);
- unmap_domain_page_global(VLAPIC(v)->regs);
- free_domheap_page(VLAPIC(v)->regs_page);
- xfree(VLAPIC(v));
- }
- hvm_release_assist_channel(v);
- }
-
- kill_timer(&d->arch.hvm_domain.pl_time.periodic_tm.timer);
- rtc_deinit(d);
- pmtimer_deinit(d);
-
- if ( d->arch.hvm_domain.shared_page_va )
- unmap_domain_page_global(
- (void *)d->arch.hvm_domain.shared_page_va);
-
- if ( d->arch.hvm_domain.buffered_io_va )
- unmap_domain_page_global((void *)d->arch.hvm_domain.buffered_io_va);
-}
-
-
static void svm_migrate_timers(struct vcpu *v)
{
struct periodic_time *pt =
@@ -943,10 +855,8 @@ static void svm_migrate_timers(struct vcpu *v)
if ( pt->enabled )
{
migrate_timer(&pt->timer, v->processor);
- migrate_timer(&v->arch.hvm_vcpu.hlt_timer, v->processor);
}
- if ( VLAPIC(v) != NULL )
- migrate_timer(&VLAPIC(v)->vlapic_timer, v->processor);
+ migrate_timer(&vcpu_vlapic(v)->vlapic_timer, v->processor);
migrate_timer(&vrtc->second_timer, v->processor);
migrate_timer(&vrtc->second_timer2, v->processor);
migrate_timer(&vpmt->timer, v->processor);
@@ -1074,8 +984,7 @@ static void svm_vmexit_do_cpuid(struct vmcb_struct *vmcb, unsigned long input,
cpuid(input, &eax, &ebx, &ecx, &edx);
if (input == 0x00000001 || input == 0x80000001 )
{
- if ( !hvm_apic_support(v->domain) ||
- !vlapic_global_enabled((VLAPIC(v))) )
+ if ( !vlapic_global_enabled(vcpu_vlapic(v)) )
{
/* Since the apic is disabled, avoid any confusion
about SMP cpus being available */
@@ -1578,9 +1487,8 @@ static int svm_set_cr0(unsigned long value)
{
/* Here the PAE is should to be opened */
HVM_DBG_LOG(DBG_LEVEL_1, "Enable the Long mode\n");
- set_bit(SVM_CPU_STATE_LMA_ENABLED,
- &v->arch.hvm_svm.cpu_state);
- vmcb->efer |= (EFER_LMA | EFER_LME);
+ set_bit(SVM_CPU_STATE_LMA_ENABLED, &v->arch.hvm_svm.cpu_state);
+ vmcb->efer |= EFER_LMA;
}
#endif /* __x86_64__ */
@@ -1621,6 +1529,11 @@ static int svm_set_cr0(unsigned long value)
}
else if ( (value & (X86_CR0_PE | X86_CR0_PG)) == X86_CR0_PE )
{
+ if ( svm_long_mode_enabled(v) )
+ {
+ vmcb->efer &= ~EFER_LMA;
+ clear_bit(SVM_CPU_STATE_LMA_ENABLED, &v->arch.hvm_svm.cpu_state);
+ }
/* we should take care of this kind of situation */
shadow_update_paging_modes(v);
vmcb->cr3 = v->arch.hvm_vcpu.hw_cr3;
@@ -1637,7 +1550,7 @@ static void mov_from_cr(int cr, int gp, struct cpu_user_regs *regs)
{
unsigned long value = 0;
struct vcpu *v = current;
- struct vlapic *vlapic = VLAPIC(v);
+ struct vlapic *vlapic = vcpu_vlapic(v);
struct vmcb_struct *vmcb;
vmcb = v->arch.hvm_svm.vmcb;
@@ -1664,8 +1577,6 @@ static void mov_from_cr(int cr, int gp, struct cpu_user_regs *regs)
printk("CR4 read=%lx\n", value);
break;
case 8:
- if ( vlapic == NULL )
- break;
value = (unsigned long)vlapic_get_reg(vlapic, APIC_TASKPRI);
value = (value & 0xF0) >> 4;
break;
@@ -1694,7 +1605,7 @@ static int mov_to_cr(int gpreg, int cr, struct cpu_user_regs *regs)
unsigned long value;
unsigned long old_cr;
struct vcpu *v = current;
- struct vlapic *vlapic = VLAPIC(v);
+ struct vlapic *vlapic = vcpu_vlapic(v);
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
ASSERT(vmcb);
@@ -1834,10 +1745,7 @@ static int mov_to_cr(int gpreg, int cr, struct cpu_user_regs *regs)
case 8:
{
- if ( vlapic == NULL )
- break;
vlapic_set_reg(vlapic, APIC_TASKPRI, ((value & 0x0F) << 4));
- vlapic_update_ppr(vlapic);
break;
}
@@ -1995,7 +1903,7 @@ static inline void svm_do_msr_access(
msr_content = vmcb->sysenter_eip;
break;
case MSR_IA32_APICBASE:
- msr_content = VLAPIC(v) ? VLAPIC(v)->apic_base_msr : 0;
+ msr_content = vcpu_vlapic(v)->apic_base_msr;
break;
default:
if (long_mode_do_msr_read(regs))
@@ -2034,7 +1942,7 @@ static inline void svm_do_msr_access(
vmcb->sysenter_eip = msr_content;
break;
case MSR_IA32_APICBASE:
- vlapic_msr_set(VLAPIC(v), msr_content);
+ vlapic_msr_set(vcpu_vlapic(v), msr_content);
break;
default:
if ( !long_mode_do_msr_write(regs) )
@@ -2546,10 +2454,10 @@ void walk_shadow_and_guest_pt(unsigned long gva)
l1_pgentry_t spte;
struct vcpu *v = current;
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
- unsigned long gpa;
+ paddr_t gpa;
gpa = shadow_gva_to_gpa(current, gva);
- printk( "gva = %lx, gpa=%lx, gCR3=%x\n", gva, gpa, (u32)vmcb->cr3 );
+ printk("gva = %lx, gpa=%"PRIpaddr", gCR3=%x\n", gva, gpa, (u32)vmcb->cr3);
if( !svm_paging_enabled(v) || mmio_space(gpa) )
return;
@@ -2597,7 +2505,6 @@ asmlinkage void svm_vmexit_handler(struct cpu_user_regs *regs)
exit_reason = vmcb->exitcode;
save_svm_cpu_user_regs(v, regs);
- vmcb->tlb_control = 1;
v->arch.hvm_svm.inject_event = 0;
if (exit_reason == VMEXIT_INVALID)
diff --git a/xen/arch/x86/hvm/svm/vmcb.c b/xen/arch/x86/hvm/svm/vmcb.c
index 209eca0407..fb246ae0ad 100644
--- a/xen/arch/x86/hvm/svm/vmcb.c
+++ b/xen/arch/x86/hvm/svm/vmcb.c
@@ -38,73 +38,64 @@
#include <xen/keyhandler.h>
extern int svm_dbg_on;
-extern int asidpool_assign_next( struct vmcb_struct *vmcb, int retire_current,
- int oldcore, int newcore);
+extern int asidpool_assign_next(
+ struct vmcb_struct *vmcb, int retire_current, int oldcore, int newcore);
#define GUEST_SEGMENT_LIMIT 0xffffffff
#define IOPM_SIZE (12 * 1024)
#define MSRPM_SIZE (8 * 1024)
-/* VMCBs and HSAs are architecturally defined to be a 4K page each */
-#define VMCB_ORDER 0
-#define HSA_ORDER 0
-
-
struct vmcb_struct *alloc_vmcb(void)
{
- struct vmcb_struct *vmcb = alloc_xenheap_pages(VMCB_ORDER);
+ struct vmcb_struct *vmcb;
- if (!vmcb) {
- printk("Warning: failed to allocate vmcb.\n");
+ vmcb = alloc_xenheap_page();
+ if ( vmcb == NULL )
+ {
+ printk(XENLOG_WARNING "Warning: failed to allocate vmcb.\n");
return NULL;
}
- memset(vmcb, 0, (PAGE_SIZE << VMCB_ORDER));
+ memset(vmcb, 0, PAGE_SIZE);
return vmcb;
}
-
void free_vmcb(struct vmcb_struct *vmcb)
{
- ASSERT(vmcb);
- free_xenheap_pages(vmcb, VMCB_ORDER);
+ free_xenheap_page(vmcb);
}
-
struct host_save_area *alloc_host_save_area(void)
{
- struct host_save_area *hsa = alloc_xenheap_pages(HSA_ORDER);
+ struct host_save_area *hsa;
- if (!hsa) {
- printk("Warning: failed to allocate vmcb.\n");
+ hsa = alloc_xenheap_page();
+ if ( hsa == NULL )
+ {
+ printk(XENLOG_WARNING "Warning: failed to allocate vmcb.\n");
return NULL;
}
- memset(hsa, 0, (PAGE_SIZE << HSA_ORDER));
+ memset(hsa, 0, PAGE_SIZE);
return hsa;
}
-
void free_host_save_area(struct host_save_area *hsa)
{
- ASSERT(hsa);
- free_xenheap_pages(hsa, HSA_ORDER);
+ free_xenheap_page(hsa);
}
-
-/* Set up intercepts to exit the guest into the hypervisor when we want it. */
-static int construct_vmcb_controls(struct arch_svm_struct *arch_svm)
+static int construct_vmcb(struct vcpu *v)
{
- struct vmcb_struct *vmcb;
- u32 *iopm;
- u32 *msrpm;
-
- vmcb = arch_svm->vmcb;
+ struct arch_svm_struct *arch_svm = &v->arch.hvm_svm;
+ struct vmcb_struct *vmcb = arch_svm->vmcb;
+ segment_attributes_t attrib;
- ASSERT(vmcb);
+ /* Always flush the TLB on VMRUN. */
+ vmcb->tlb_control = 1;
- /* mask off all general 1 intercepts except those listed here */
+ /* SVM intercepts. */
vmcb->general1_intercepts =
GENERAL1_INTERCEPT_INTR | GENERAL1_INTERCEPT_NMI |
GENERAL1_INTERCEPT_SMI | GENERAL1_INTERCEPT_INIT |
@@ -112,75 +103,46 @@ static int construct_vmcb_controls(struct arch_svm_struct *arch_svm)
GENERAL1_INTERCEPT_HLT | GENERAL1_INTERCEPT_INVLPG |
GENERAL1_INTERCEPT_INVLPGA | GENERAL1_INTERCEPT_IOIO_PROT |
GENERAL1_INTERCEPT_MSR_PROT | GENERAL1_INTERCEPT_SHUTDOWN_EVT;
-
- /* turn on the general 2 intercepts */
vmcb->general2_intercepts =
GENERAL2_INTERCEPT_VMRUN | GENERAL2_INTERCEPT_VMMCALL |
GENERAL2_INTERCEPT_VMLOAD | GENERAL2_INTERCEPT_VMSAVE |
GENERAL2_INTERCEPT_STGI | GENERAL2_INTERCEPT_CLGI |
GENERAL2_INTERCEPT_SKINIT | GENERAL2_INTERCEPT_RDTSCP;
- /* read or write all debug registers 0 - 15 */
+ /* Intercept all debug-register writes. */
vmcb->dr_intercepts = DR_INTERCEPT_ALL_WRITES;
- /* RD/WR all control registers 0 - 15, but not read CR2 */
+ /* Intercept all control-register accesses, except to CR2. */
vmcb->cr_intercepts = ~(CR_INTERCEPT_CR2_READ | CR_INTERCEPT_CR2_WRITE);
- /* The following is for I/O and MSR permision map */
- iopm = alloc_xenheap_pages(get_order_from_bytes(IOPM_SIZE));
- if (iopm)
+ /* I/O and MSR permission bitmaps. */
+ arch_svm->iopm = alloc_xenheap_pages(get_order_from_bytes(IOPM_SIZE));
+ arch_svm->msrpm = alloc_xenheap_pages(get_order_from_bytes(MSRPM_SIZE));
+ if ( (arch_svm->iopm == NULL) || (arch_svm->msrpm == NULL) )
{
- memset(iopm, 0xff, IOPM_SIZE);
- clear_bit(PC_DEBUG_PORT, iopm);
+ free_xenheap_pages(arch_svm->iopm, get_order_from_bytes(IOPM_SIZE));
+ free_xenheap_pages(arch_svm->msrpm, get_order_from_bytes(MSRPM_SIZE));
+ return -ENOMEM;
}
- msrpm = alloc_xenheap_pages(get_order_from_bytes(MSRPM_SIZE));
- if (msrpm)
- memset(msrpm, 0xff, MSRPM_SIZE);
+ memset(arch_svm->iopm, 0xff, IOPM_SIZE);
+ clear_bit(PC_DEBUG_PORT, arch_svm->iopm);
+ memset(arch_svm->msrpm, 0xff, MSRPM_SIZE);
+ vmcb->iopm_base_pa = (u64)virt_to_maddr(arch_svm->iopm);
+ vmcb->msrpm_base_pa = (u64)virt_to_maddr(arch_svm->msrpm);
- arch_svm->iopm = iopm;
- arch_svm->msrpm = msrpm;
-
- if (! iopm || ! msrpm)
- return 1;
-
- vmcb->iopm_base_pa = (u64) virt_to_maddr(iopm);
- vmcb->msrpm_base_pa = (u64) virt_to_maddr(msrpm);
-
- return 0;
-}
-
-
-/*
- * Initially set the same environement as host.
- */
-static int construct_init_vmcb_guest(struct arch_svm_struct *arch_svm,
- struct cpu_user_regs *regs )
-{
- int error = 0;
- unsigned long crn;
- segment_attributes_t attrib;
- unsigned long dr7;
- unsigned long eflags;
- unsigned long shadow_cr;
- struct vmcb_struct *vmcb = arch_svm->vmcb;
-
- /* Allows IRQs to be shares */
+ /* Virtualise EFLAGS.IF and LAPIC TPR (CR8). */
vmcb->vintr.fields.intr_masking = 1;
- /* Set up event injection entry in VMCB. Just clear it. */
+ /* Initialise event injection to no-op. */
vmcb->eventinj.bytes = 0;
- /* TSC */
+ /* TSC. */
vmcb->tsc_offset = 0;
- vmcb->cs.sel = regs->cs;
- vmcb->es.sel = regs->es;
- vmcb->ss.sel = regs->ss;
- vmcb->ds.sel = regs->ds;
- vmcb->fs.sel = regs->fs;
- vmcb->gs.sel = regs->gs;
-
- /* Guest segment Limits. 64K for real mode*/
+ /* Guest EFER: *must* contain SVME or VMRUN will fail. */
+ vmcb->efer = EFER_SVME;
+
+ /* Guest segment limits. */
vmcb->cs.limit = GUEST_SEGMENT_LIMIT;
vmcb->es.limit = GUEST_SEGMENT_LIMIT;
vmcb->ss.limit = GUEST_SEGMENT_LIMIT;
@@ -188,7 +150,7 @@ static int construct_init_vmcb_guest(struct arch_svm_struct *arch_svm,
vmcb->fs.limit = GUEST_SEGMENT_LIMIT;
vmcb->gs.limit = GUEST_SEGMENT_LIMIT;
- /* Base address for segments */
+ /* Guest segment bases. */
vmcb->cs.base = 0;
vmcb->es.base = 0;
vmcb->ss.base = 0;
@@ -196,216 +158,125 @@ static int construct_init_vmcb_guest(struct arch_svm_struct *arch_svm,
vmcb->fs.base = 0;
vmcb->gs.base = 0;
- /* Guest Interrupt descriptor table */
- vmcb->idtr.base = 0;
- vmcb->idtr.limit = 0;
-
- /* Set up segment attributes */
+ /* Guest segment AR bytes. */
attrib.bytes = 0;
attrib.fields.type = 0x3; /* type = 3 */
- attrib.fields.s = 1; /* code or data, i.e. not system */
- attrib.fields.dpl = 0; /* DPL = 0 */
- attrib.fields.p = 1; /* segment present */
- attrib.fields.db = 1; /* 32-bit */
- attrib.fields.g = 1; /* 4K pages in limit */
-
- /* Data selectors */
+ attrib.fields.s = 1; /* code or data, i.e. not system */
+ attrib.fields.dpl = 0; /* DPL = 0 */
+ attrib.fields.p = 1; /* segment present */
+ attrib.fields.db = 1; /* 32-bit */
+ attrib.fields.g = 1; /* 4K pages in limit */
vmcb->es.attributes = attrib;
vmcb->ss.attributes = attrib;
vmcb->ds.attributes = attrib;
vmcb->fs.attributes = attrib;
vmcb->gs.attributes = attrib;
-
- /* Code selector */
- attrib.fields.type = 0xb; /* type=0xb -> executable/readable, accessed */
+ attrib.fields.type = 0xb; /* type=0xb -> executable/readable, accessed */
vmcb->cs.attributes = attrib;
- /* Guest Global descriptor table */
+ /* Guest IDT. */
+ vmcb->idtr.base = 0;
+ vmcb->idtr.limit = 0;
+
+ /* Guest GDT. */
vmcb->gdtr.base = 0;
vmcb->gdtr.limit = 0;
- /* Guest Local Descriptor Table */
- attrib.fields.s = 0; /* not code or data segement */
- attrib.fields.type = 0x2; /* LDT */
- attrib.fields.db = 0; /* 16-bit */
- attrib.fields.g = 0;
- vmcb->ldtr.attributes = attrib;
+ /* Guest LDT. */
+ vmcb->ldtr.sel = 0;
+ vmcb->ldtr.base = 0;
+ vmcb->ldtr.limit = 0;
+ vmcb->ldtr.attributes.bytes = 0;
+ /* Guest TSS. */
attrib.fields.type = 0xb; /* 32-bit TSS (busy) */
vmcb->tr.attributes = attrib;
vmcb->tr.base = 0;
vmcb->tr.limit = 0xff;
- __asm__ __volatile__ ("mov %%cr0,%0" : "=r" (crn) :);
- vmcb->cr0 = crn;
-
- /* Initally PG, PE are not set*/
- shadow_cr = vmcb->cr0;
- shadow_cr &= ~X86_CR0_PG;
- arch_svm->cpu_shadow_cr0 = shadow_cr;
-
- /* CR3 is set in svm_final_setup_guest */
+ /* Guest CR0. */
+ vmcb->cr0 = read_cr0();
+ arch_svm->cpu_shadow_cr0 = vmcb->cr0 & ~(X86_CR0_PG | X86_CR0_TS);
- __asm__ __volatile__ ("mov %%cr4,%0" : "=r" (crn) :);
- crn &= ~(X86_CR4_PGE | X86_CR4_PSE | X86_CR4_PAE);
- arch_svm->cpu_shadow_cr4 = crn;
- vmcb->cr4 = crn | SVM_CR4_HOST_MASK;
+ /* Guest CR4. */
+ arch_svm->cpu_shadow_cr4 =
+ read_cr4() & ~(X86_CR4_PGE | X86_CR4_PSE | X86_CR4_PAE);
+ vmcb->cr4 = arch_svm->cpu_shadow_cr4 | SVM_CR4_HOST_MASK;
- vmcb->rsp = 0;
- vmcb->rip = regs->eip;
-
- eflags = regs->eflags & ~HVM_EFLAGS_RESERVED_0; /* clear 0s */
- eflags |= HVM_EFLAGS_RESERVED_1; /* set 1s */
-
- vmcb->rflags = eflags;
+ shadow_update_paging_modes(v);
+ vmcb->cr3 = v->arch.hvm_vcpu.hw_cr3;
- __asm__ __volatile__ ("mov %%dr7, %0\n" : "=r" (dr7));
- vmcb->dr7 = dr7;
+ arch_svm->vmcb->exception_intercepts = MONITOR_DEFAULT_EXCEPTION_BITMAP;
- return error;
+ return 0;
}
-
-/*
- * destroy the vmcb.
- */
-
-void destroy_vmcb(struct arch_svm_struct *arch_svm)
+int svm_create_vmcb(struct vcpu *v)
{
- if(arch_svm->vmcb != NULL)
+ struct arch_svm_struct *arch_svm = &v->arch.hvm_svm;
+ int rc;
+
+ if ( (arch_svm->vmcb = alloc_vmcb()) == NULL )
{
- asidpool_retire(arch_svm->vmcb, arch_svm->asid_core);
- free_vmcb(arch_svm->vmcb);
- }
- if(arch_svm->iopm != NULL) {
- free_xenheap_pages(
- arch_svm->iopm, get_order_from_bytes(IOPM_SIZE));
- arch_svm->iopm = NULL;
+ printk("Failed to create a new VMCB\n");
+ return -ENOMEM;
}
- if(arch_svm->msrpm != NULL) {
- free_xenheap_pages(
- arch_svm->msrpm, get_order_from_bytes(MSRPM_SIZE));
- arch_svm->msrpm = NULL;
+
+ if ( (rc = construct_vmcb(v)) != 0 )
+ {
+ free_vmcb(arch_svm->vmcb);
+ arch_svm->vmcb = NULL;
+ return rc;
}
- arch_svm->vmcb = NULL;
-}
+ arch_svm->vmcb_pa = virt_to_maddr(arch_svm->vmcb);
-/*
- * construct the vmcb.
- */
+ return 0;
+}
-int construct_vmcb(struct arch_svm_struct *arch_svm,
- struct cpu_user_regs *regs)
+void svm_destroy_vmcb(struct vcpu *v)
{
- int error;
- long rc=0;
-
- memset(arch_svm, 0, sizeof(struct arch_svm_struct));
+ struct arch_svm_struct *arch_svm = &v->arch.hvm_svm;
- if (!(arch_svm->vmcb = alloc_vmcb())) {
- printk("Failed to create a new VMCB\n");
- rc = -ENOMEM;
- goto err_out;
+ if ( arch_svm->vmcb != NULL )
+ {
+ asidpool_retire(arch_svm->vmcb, arch_svm->asid_core);
+ free_vmcb(arch_svm->vmcb);
}
- arch_svm->vmcb_pa = (u64) virt_to_maddr(arch_svm->vmcb);
-
- if ((error = construct_vmcb_controls(arch_svm)))
+ if ( arch_svm->iopm != NULL )
{
- printk("construct_vmcb: construct_vmcb_controls failed\n");
- rc = -EINVAL;
- goto err_out;
+ free_xenheap_pages(
+ arch_svm->iopm, get_order_from_bytes(IOPM_SIZE));
+ arch_svm->iopm = NULL;
}
- /* guest selectors */
- if ((error = construct_init_vmcb_guest(arch_svm, regs)))
+ if ( arch_svm->msrpm != NULL )
{
- printk("construct_vmcb: construct_vmcb_guest failed\n");
- rc = -EINVAL;
- goto err_out;
+ free_xenheap_pages(
+ arch_svm->msrpm, get_order_from_bytes(MSRPM_SIZE));
+ arch_svm->msrpm = NULL;
}
- arch_svm->vmcb->exception_intercepts = MONITOR_DEFAULT_EXCEPTION_BITMAP;
- if (regs->eflags & EF_TF)
- arch_svm->vmcb->exception_intercepts |= EXCEPTION_BITMAP_DB;
- else
- arch_svm->vmcb->exception_intercepts &= ~EXCEPTION_BITMAP_DB;
-
- return 0;
-
-err_out:
- destroy_vmcb(arch_svm);
- return rc;
+ arch_svm->vmcb = NULL;
}
-
void svm_do_launch(struct vcpu *v)
{
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
int core = smp_processor_id();
- ASSERT(vmcb);
- /* Update CR3, GDT, LDT, TR */
hvm_stts(v);
/* current core is the one we intend to perform the VMRUN on */
v->arch.hvm_svm.launch_core = v->arch.hvm_svm.asid_core = core;
clear_bit(ARCH_SVM_VMCB_ASSIGN_ASID, &v->arch.hvm_svm.flags);
- if ( !asidpool_assign_next( vmcb, 0, core, core ))
+ if ( !asidpool_assign_next(vmcb, 0, core, core) )
BUG();
- if (v->vcpu_id == 0)
- hvm_setup_platform(v->domain);
-
- if (hvm_apic_support(v->domain))
- vlapic_init(v);
- init_timer(&v->arch.hvm_vcpu.hlt_timer, hlt_timer_fn, v, v->processor);
-
- vmcb->ldtr.sel = 0;
- vmcb->ldtr.base = 0;
- vmcb->ldtr.limit = 0;
- vmcb->ldtr.attributes.bytes = 0;
-
- vmcb->efer = EFER_SVME; /* Make sure VMRUN won't return with -1 */
-
- if (svm_dbg_on)
- {
- unsigned long pt;
- printk("%s: hw_cr3 = %llx\n", __func__,
- (unsigned long long) v->arch.hvm_vcpu.hw_cr3);
- pt = pagetable_get_paddr(v->arch.guest_table);
- printk("%s: guest_table = %lx\n", __func__, pt);
- pt = pagetable_get_paddr(v->domain->arch.phys_table);
- printk("%s: phys_table = %lx\n", __func__, pt);
- }
-
- /* Set cr3 from hw_cr3 even when guest-visible paging is not enabled */
- vmcb->cr3 = v->arch.hvm_vcpu.hw_cr3;
-
- if (svm_dbg_on)
- {
- printk("%s: cr3 = %lx ", __func__, (unsigned long)vmcb->cr3);
- printk("init_guest_table: guest_table = 0x%08x, monitor_table = 0x%08x,"
- " hw_cr3 = 0x%16llx\n", (int)v->arch.guest_table.pfn,
- (int)v->arch.monitor_table.pfn,
- (unsigned long long) v->arch.hvm_vcpu.hw_cr3);
- }
-
v->arch.schedule_tail = arch_svm_do_resume;
-
- v->arch.hvm_svm.saved_irq_vector = -1;
-
- hvm_set_guest_time(v, 0);
-
- if (svm_dbg_on)
- svm_dump_vmcb(__func__, vmcb);
-
- vmcb->tlb_control = 1;
}
-
-
static void svm_dump_sel(char *name, segment_selector_t *s)
{
printk("%s: sel=0x%04x, attr=0x%04x, limit=0x%08x, base=0x%016llx\n",
@@ -413,7 +284,6 @@ static void svm_dump_sel(char *name, segment_selector_t *s)
(unsigned long long)s->base);
}
-
void svm_dump_vmcb(const char *from, struct vmcb_struct *vmcb)
{
printk("Dumping guest's current state at %s...\n", from);
@@ -482,20 +352,14 @@ static void vmcb_dump(unsigned char ch)
struct vcpu *v;
printk("*********** VMCB Areas **************\n");
- for_each_domain(d) {
+ for_each_domain ( d )
+ {
+ if ( !is_hvm_domain(d) )
+ continue;
printk("\n>>> Domain %d <<<\n", d->domain_id);
- for_each_vcpu(d, v) {
-
- /*
- * Presumably, if a domain is not an HVM guest,
- * the very first CPU will not pass this test
- */
- if (!hvm_guest(v)) {
- printk("\t\tNot HVM guest\n");
- break;
- }
+ for_each_vcpu ( d, v )
+ {
printk("\tVCPU %d\n", v->vcpu_id);
-
svm_dump_vmcb("key_handler", v->arch.hvm_svm.vmcb);
}
}
diff --git a/xen/arch/x86/hvm/svm/x86_32/exits.S b/xen/arch/x86/hvm/svm/x86_32/exits.S
index 36fa80b680..2cd913e16b 100644
--- a/xen/arch/x86/hvm/svm/x86_32/exits.S
+++ b/xen/arch/x86/hvm/svm/x86_32/exits.S
@@ -34,7 +34,7 @@
* At VMExit time the processor saves the guest selectors, esp, eip,
* and eflags. Therefore we don't save them, but simply decrement
* the kernel stack pointer to make it consistent with the stack frame
- * at usual interruption time. The eflags of the host is not saved by VMX,
+ * at usual interruption time. The eflags of the host is not saved by AMD-V,
* and we set it to the fixed value.
*
* We also need the room, especially because orig_eax field is used
@@ -89,8 +89,8 @@
#define CLGI .byte 0x0F,0x01,0xDD
ENTRY(svm_asm_do_launch)
- sti
CLGI
+ sti
GET_CURRENT(%ebx)
movl VCPU_svm_vmcb(%ebx), %ecx
movl 24(%esp), %eax
@@ -152,9 +152,8 @@ svm_restore_all_guest:
call svm_intr_assist
call svm_asid
call svm_load_cr2
- sti
/*
- * Check if we are going back to SVM-based VM
+ * Check if we are going back to AMD-V based VM
* By this time, all the setups in the VMCB must be complete.
*/
jmp svm_asm_do_launch
diff --git a/xen/arch/x86/hvm/svm/x86_64/exits.S b/xen/arch/x86/hvm/svm/x86_64/exits.S
index 823c02378d..0c9aa641a3 100644
--- a/xen/arch/x86/hvm/svm/x86_64/exits.S
+++ b/xen/arch/x86/hvm/svm/x86_64/exits.S
@@ -1,5 +1,5 @@
/*
- * exits.S: SVM architecture-specific exit handling.
+ * exits.S: AMD-V architecture-specific exit handling.
* Copyright (c) 2004, Intel Corporation.
* Copyright (c) 2005, AMD Corporation.
*
@@ -34,7 +34,7 @@
* At VMExit time the processor saves the guest selectors, rsp, rip,
* and rflags. Therefore we don't save them, but simply decrement
* the kernel stack pointer to make it consistent with the stack frame
- * at usual interruption time. The rflags of the host is not saved by VMX,
+ * at usual interruption time. The rflags of the host is not saved by AMD-V,
* and we set it to the fixed value.
*
* We also need the room, especially because orig_eax field is used
@@ -99,8 +99,8 @@
#define CLGI .byte 0x0F,0x01,0xDD
ENTRY(svm_asm_do_launch)
- sti
CLGI
+ sti
GET_CURRENT(%rbx)
movq VCPU_svm_vmcb(%rbx), %rcx
movq UREGS_rax(%rsp), %rax
@@ -165,10 +165,9 @@ svm_restore_all_guest:
call svm_intr_assist
call svm_asid
call svm_load_cr2
- sti
/*
- * Check if we are going back to VMX-based VM
- * By this time, all the setups in the VMCS must be complete.
+ * Check if we are going back to AMD-V based VM
+ * By this time, all the setups in the VMCB must be complete.
*/
jmp svm_asm_do_launch
diff --git a/xen/arch/x86/hvm/vioapic.c b/xen/arch/x86/hvm/vioapic.c
index e608b24fbf..dc7f29d820 100644
--- a/xen/arch/x86/hvm/vioapic.c
+++ b/xen/arch/x86/hvm/vioapic.c
@@ -1,31 +1,29 @@
/*
-* Copyright (C) 2001 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
-*/
-
-/*
-* Yunhong Jiang <yunhong.jiang@intel.com>
-* Ported to xen by using virtual IRQ line.
-*/
+ * Copyright (C) 2001 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
+ *
+ * Yunhong Jiang <yunhong.jiang@intel.com>
+ * Ported to xen by using virtual IRQ line.
+ */
#include <xen/config.h>
#include <xen/types.h>
@@ -39,6 +37,7 @@
#include <asm/hvm/vpic.h>
#include <asm/hvm/support.h>
#include <asm/current.h>
+#include <asm/event.h>
/* HACK: Route IRQ0 only to VCPU0 to prevent time jumps. */
#define IRQ0_SPECIAL_ROUTING 1
@@ -50,204 +49,179 @@ static int redir_warning_done = 0;
#define opt_hvm_debug_level opt_vmx_debug_level
#endif
-static void ioapic_enable(hvm_vioapic_t *s, uint8_t enable)
-{
- if (enable)
- s->flags |= IOAPIC_ENABLE_FLAG;
- else
- s->flags &= ~IOAPIC_ENABLE_FLAG;
-}
-
-#ifdef HVM_DOMAIN_SAVE_RESTORE
-void ioapic_save(QEMUFile* f, void* opaque)
-{
- printk("no implementation for ioapic_save\n");
-}
-
-int ioapic_load(QEMUFile* f, void* opaque, int version_id)
-{
- printk("no implementation for ioapic_load\n");
- return 0;
-}
-#endif
-
-static unsigned long hvm_vioapic_read_indirect(struct hvm_vioapic *s,
- unsigned long addr,
- unsigned long length)
+static unsigned long vioapic_read_indirect(struct vioapic *vioapic,
+ unsigned long addr,
+ unsigned long length)
{
unsigned long result = 0;
- ASSERT(s);
-
- switch (s->ioregsel) {
- case IOAPIC_REG_VERSION:
- result = ((((IOAPIC_NUM_PINS-1) & 0xff) << 16)
- | (IOAPIC_VERSION_ID & 0xff));
- break;
-
-#ifndef __ia64__
- case IOAPIC_REG_APIC_ID:
- result = ((s->id & 0xf) << 24);
+ switch ( vioapic->ioregsel )
+ {
+ case VIOAPIC_REG_VERSION:
+ result = ((((VIOAPIC_NUM_PINS-1) & 0xff) << 16)
+ | (VIOAPIC_VERSION_ID & 0xff));
break;
- case IOAPIC_REG_ARB_ID:
- /* XXX how arb_id used on p4? */
- result = ((s->arb_id & 0xf) << 24);
+#if !VIOAPIC_IS_IOSAPIC
+ case VIOAPIC_REG_APIC_ID:
+ case VIOAPIC_REG_ARB_ID:
+ result = ((vioapic->id & 0xf) << 24);
break;
#endif
default:
- {
- uint32_t redir_index = 0;
- uint64_t redir_content = 0;
-
- redir_index = (s->ioregsel - 0x10) >> 1;
-
- if (redir_index >= 0 && redir_index < IOAPIC_NUM_PINS) {
- redir_content = s->redirtbl[redir_index].value;
+ {
+ uint32_t redir_index = (vioapic->ioregsel - 0x10) >> 1;
+ uint64_t redir_content;
- result = (s->ioregsel & 0x1)?
- (redir_content >> 32) & 0xffffffff :
- redir_content & 0xffffffff;
- } else {
- printk("apic_mem_readl:undefined ioregsel %x\n",
- s->ioregsel);
- domain_crash_synchronous();
- }
+ if ( redir_index >= VIOAPIC_NUM_PINS )
+ {
+ gdprintk(XENLOG_WARNING, "apic_mem_readl:undefined ioregsel %x\n",
+ vioapic->ioregsel);
break;
}
- } /* switch */
+
+ redir_content = vioapic->redirtbl[redir_index].bits;
+ result = (vioapic->ioregsel & 0x1)?
+ (redir_content >> 32) & 0xffffffff :
+ redir_content & 0xffffffff;
+ break;
+ }
+ }
return result;
}
-static unsigned long hvm_vioapic_read(struct vcpu *v,
- unsigned long addr,
- unsigned long length)
+static unsigned long vioapic_read(struct vcpu *v,
+ unsigned long addr,
+ unsigned long length)
{
- struct hvm_vioapic *s = &(v->domain->arch.hvm_domain.vioapic);
- uint32_t result = 0;
-
- HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "hvm_vioapic_read addr %lx\n", addr);
+ struct vioapic *vioapic = domain_vioapic(v->domain);
+ uint32_t result;
- ASSERT(s);
+ HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "vioapic_read addr %lx\n", addr);
addr &= 0xff;
- switch (addr) {
- case IOAPIC_REG_SELECT:
- result = s->ioregsel;
+ switch ( addr )
+ {
+ case VIOAPIC_REG_SELECT:
+ result = vioapic->ioregsel;
break;
- case IOAPIC_REG_WINDOW:
- result = hvm_vioapic_read_indirect(s, addr, length);
+ case VIOAPIC_REG_WINDOW:
+ result = vioapic_read_indirect(vioapic, addr, length);
break;
default:
- break;
+ result = 0;
+ break;
}
return result;
}
-static void hvm_vioapic_update_imr(struct hvm_vioapic *s, int index)
+static void vioapic_update_imr(struct vioapic *vioapic, int index)
{
- if (s->redirtbl[index].RedirForm.mask)
- set_bit(index, &s->imr);
- else
- clear_bit(index, &s->imr);
+ if ( vioapic->redirtbl[index].fields.mask )
+ set_bit(index, &vioapic->imr);
+ else
+ clear_bit(index, &vioapic->imr);
}
-static void hvm_vioapic_write_indirect(struct hvm_vioapic *s,
- unsigned long addr,
- unsigned long length,
- unsigned long val)
+static void vioapic_write_indirect(struct vioapic *vioapic,
+ unsigned long addr,
+ unsigned long length,
+ unsigned long val)
{
- switch (s->ioregsel) {
- case IOAPIC_REG_VERSION:
- printk("hvm_vioapic_write_indirect: version register read only\n");
+ switch ( vioapic->ioregsel )
+ {
+ case VIOAPIC_REG_VERSION:
+ /* Writes are ignored. */
break;
-#ifndef __ia64__
- case IOAPIC_REG_APIC_ID:
- s->id = (val >> 24) & 0xf;
+#if !VIOAPIC_IS_IOSAPIC
+ case VIOAPIC_REG_APIC_ID:
+ vioapic->id = (val >> 24) & 0xf;
break;
- case IOAPIC_REG_ARB_ID:
- s->arb_id = val;
+ case VIOAPIC_REG_ARB_ID:
break;
#endif
default:
- {
- uint32_t redir_index = 0;
-
- redir_index = (s->ioregsel - 0x10) >> 1;
+ {
+ uint32_t redir_index = (vioapic->ioregsel - 0x10) >> 1;
+ uint64_t redir_content;
- HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "hvm_vioapic_write_indirect "
- "change redir index %x val %lx\n",
- redir_index, val);
+ HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "vioapic_write_indirect "
+ "change redir index %x val %lx\n",
+ redir_index, val);
- if (redir_index >= 0 && redir_index < IOAPIC_NUM_PINS) {
- uint64_t redir_content;
+ if ( redir_index >= VIOAPIC_NUM_PINS )
+ {
+ gdprintk(XENLOG_WARNING, "vioapic_write_indirect "
+ "error register %x\n", vioapic->ioregsel);
+ break;
+ }
- redir_content = s->redirtbl[redir_index].value;
+ redir_content = vioapic->redirtbl[redir_index].bits;
- if (s->ioregsel & 0x1) {
+ if ( vioapic->ioregsel & 0x1 )
+ {
#ifdef IRQ0_SPECIAL_ROUTING
- if ( !redir_warning_done && (redir_index == 0) &&
- ((val >> 24) != 0) ) {
- /*
- * Cannot yet handle delivering PIT interrupts to
- * any VCPU != 0. Needs proper fixing, but for now
- * simply spit a warning that we're going to ignore
- * the target in practice & always deliver to VCPU 0
- */
- printk("IO-APIC: PIT (IRQ0) redirect to VCPU %lx "
- "will be ignored.\n", val >> 24);
- redir_warning_done = 1;
- }
-#endif
- redir_content = (((uint64_t)val & 0xffffffff) << 32) |
- (redir_content & 0xffffffff);
- } else
- redir_content = ((redir_content >> 32) << 32) |
- (val & 0xffffffff);
- s->redirtbl[redir_index].value = redir_content;
- hvm_vioapic_update_imr(s, redir_index);
- } else {
- printk("hvm_vioapic_write_indirect "
- "error register %x\n", s->ioregsel);
+ if ( !redir_warning_done && (redir_index == 0) &&
+ ((val >> 24) != 0) )
+ {
+ /*
+ * Cannot yet handle delivering PIT interrupts to any VCPU !=
+ * 0. Needs proper fixing, but for now simply spit a warning
+ * that we're going to ignore the target in practice and always
+ * deliver to VCPU 0.
+ */
+ printk("IO-APIC: PIT (IRQ0) redirect to VCPU %lx "
+ "will be ignored.\n", val >> 24);
+ redir_warning_done = 1;
}
- break;
+#endif
+ redir_content = (((uint64_t)val & 0xffffffff) << 32) |
+ (redir_content & 0xffffffff);
+ }
+ else
+ {
+ redir_content = ((redir_content >> 32) << 32) |
+ (val & 0xffffffff);
}
+ vioapic->redirtbl[redir_index].bits = redir_content;
+ vioapic_update_imr(vioapic, redir_index);
+ break;
+ }
} /* switch */
}
-static void hvm_vioapic_write(struct vcpu *v,
- unsigned long addr,
- unsigned long length,
- unsigned long val)
+static void vioapic_write(struct vcpu *v,
+ unsigned long addr,
+ unsigned long length,
+ unsigned long val)
{
- hvm_vioapic_t *s = &(v->domain->arch.hvm_domain.vioapic);
-
- ASSERT(s);
+ struct vioapic *vioapic = domain_vioapic(v->domain);
addr &= 0xff;
- switch (addr) {
- case IOAPIC_REG_SELECT:
- s->ioregsel = val;
+ switch ( addr )
+ {
+ case VIOAPIC_REG_SELECT:
+ vioapic->ioregsel = val;
break;
- case IOAPIC_REG_WINDOW:
- hvm_vioapic_write_indirect(s, addr, length, val);
+ case VIOAPIC_REG_WINDOW:
+ vioapic_write_indirect(vioapic, addr, length, val);
break;
-#ifdef __ia64__
- case IOAPIC_REG_EOI:
- ioapic_update_EOI(v->domain, val);
+#if VIOAPIC_IS_IOSAPIC
+ case VIOAPIC_REG_EOI:
+ vioapic_update_EOI(v->domain, val);
break;
#endif
@@ -256,49 +230,34 @@ static void hvm_vioapic_write(struct vcpu *v,
}
}
-static int hvm_vioapic_range(struct vcpu *v, unsigned long addr)
+static int vioapic_range(struct vcpu *v, unsigned long addr)
{
- hvm_vioapic_t *s = &(v->domain->arch.hvm_domain.vioapic);
+ struct vioapic *vioapic = domain_vioapic(v->domain);
- if ((s->flags & IOAPIC_ENABLE_FLAG) &&
- (addr >= s->base_address &&
- (addr < s->base_address + IOAPIC_MEM_LENGTH)))
- return 1;
- else
- return 0;
+ return ((addr >= vioapic->base_address &&
+ (addr < vioapic->base_address + VIOAPIC_MEM_LENGTH)));
}
struct hvm_mmio_handler vioapic_mmio_handler = {
- .check_handler = hvm_vioapic_range,
- .read_handler = hvm_vioapic_read,
- .write_handler = hvm_vioapic_write
+ .check_handler = vioapic_range,
+ .read_handler = vioapic_read,
+ .write_handler = vioapic_write
};
-static void hvm_vioapic_reset(hvm_vioapic_t *s)
+static void vioapic_reset(struct vioapic *vioapic)
{
int i;
- memset(s, 0, sizeof(hvm_vioapic_t));
+ memset(vioapic, 0, sizeof(*vioapic));
- for (i = 0; i < IOAPIC_NUM_PINS; i++) {
- s->redirtbl[i].RedirForm.mask = 0x1;
- hvm_vioapic_update_imr(s, i);
+ for ( i = 0; i < VIOAPIC_NUM_PINS; i++ )
+ {
+ vioapic->redirtbl[i].fields.mask = 0x1;
+ vioapic_update_imr(vioapic, i);
}
}
-static void ioapic_update_config(hvm_vioapic_t *s,
- unsigned long address,
- uint8_t enable)
-{
- ASSERT(s);
-
- ioapic_enable(s, enable);
-
- if (address != s->base_address)
- s->base_address = address;
-}
-
-static int ioapic_inj_irq(hvm_vioapic_t *s,
+static int ioapic_inj_irq(struct vioapic *vioapic,
struct vlapic * target,
uint8_t vector,
uint8_t trig_mode,
@@ -306,161 +265,157 @@ static int ioapic_inj_irq(hvm_vioapic_t *s,
{
int result = 0;
- ASSERT(s && target);
-
HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "ioapic_inj_irq "
- "irq %d trig %d delive mode %d\n",
- vector, trig_mode, delivery_mode);
+ "irq %d trig %d delive mode %d\n",
+ vector, trig_mode, delivery_mode);
- switch (delivery_mode) {
+ switch ( delivery_mode )
+ {
case dest_Fixed:
case dest_LowestPrio:
- if (vlapic_set_irq(target, vector, trig_mode) && (trig_mode == 1))
- printk("<ioapic_inj_irq> level interrupt happen before cleared\n");
+ if ( vlapic_set_irq(target, vector, trig_mode) && (trig_mode == 1) )
+ gdprintk(XENLOG_WARNING, "level interrupt before cleared\n");
result = 1;
break;
default:
- printk("<ioapic_inj_irq> error delivery mode %d\n",
- delivery_mode);
+ gdprintk(XENLOG_WARNING, "error delivery mode %d\n", delivery_mode);
break;
- }
+ }
- return result;
+ return result;
}
#ifndef __ia64__
-static int ioapic_match_logical_addr(hvm_vioapic_t *s, int number, uint8_t dest)
+static int vlapic_match_logical_addr(struct vlapic *vlapic, uint8_t dest)
{
int result = 0;
- uint32_t logical_dest = vlapic_get_reg(s->lapic_info[number], APIC_LDR);
+ uint32_t logical_dest;
- ASSERT(s && s->lapic_info[number]);
+ HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "vlapic_match_logical_addr "
+ "vcpu=%d vlapic_id=%x dest=%x\n",
+ vlapic_vcpu(vlapic)->vcpu_id, VLAPIC_ID(vlapic), dest);
- HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "ioapic_match_logical_addr "
- "number %i dest %x\n",
- number, dest);
+ logical_dest = vlapic_get_reg(vlapic, APIC_LDR);
- switch (vlapic_get_reg(s->lapic_info[number], APIC_DFR))
+ switch ( vlapic_get_reg(vlapic, APIC_DFR) )
{
case APIC_DFR_FLAT:
- result =
- (dest & GET_APIC_LOGICAL_ID(logical_dest)) != 0;
+ result = ((dest & GET_APIC_LOGICAL_ID(logical_dest)) != 0);
break;
case APIC_DFR_CLUSTER:
/* Should we support flat cluster mode ?*/
if ( (GET_APIC_LOGICAL_ID(logical_dest) >> 4
- == ((dest >> 0x4) & 0xf)) &&
+ == ((dest >> 0x4) & 0xf)) &&
(logical_dest & (dest & 0xf)) )
result = 1;
break;
default:
- printk("error DFR value for %x local apic\n", number);
+ gdprintk(XENLOG_WARNING, "error DFR value for lapic of vcpu %d\n",
+ vlapic_vcpu(vlapic)->vcpu_id);
break;
}
return result;
}
#else
-extern int ioapic_match_logical_addr(hvm_vioapic_t *s, int number, uint8_t dest);
+extern int vlapic_match_logical_addr(struct vlapic *vlapic, uint16_t dest);
#endif
-static uint32_t ioapic_get_delivery_bitmask(hvm_vioapic_t *s,
+static uint32_t ioapic_get_delivery_bitmask(struct vioapic *vioapic,
uint16_t dest,
uint8_t dest_mode,
uint8_t vector,
uint8_t delivery_mode)
{
uint32_t mask = 0;
- int i;
+ struct vcpu *v;
HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "ioapic_get_delivery_bitmask "
- "dest %d dest_mode %d "
- "vector %d del_mode %d, lapic_count %d\n",
- dest, dest_mode, vector, delivery_mode, s->lapic_count);
-
- ASSERT(s);
+ "dest %d dest_mode %d vector %d del_mode %d\n",
+ dest, dest_mode, vector, delivery_mode);
- if ( dest_mode == 0 )
+ if ( dest_mode == 0 ) /* Physical mode. */
{
- /* Physical mode. */
- for ( i = 0; i < s->lapic_count; i++ )
+ if ( dest == 0xFF ) /* Broadcast. */
{
- if ( VLAPIC_ID(s->lapic_info[i]) == dest )
- {
- mask = 1 << i;
- break;
- }
+ for_each_vcpu ( vioapic_domain(vioapic), v )
+ mask |= 1 << v->vcpu_id;
+ goto out;
}
- /* Broadcast. */
- if ( dest == 0xFF )
+ for_each_vcpu ( vioapic_domain(vioapic), v )
{
- for ( i = 0; i < s->lapic_count; i++ )
- mask |= ( 1 << i );
- }
- }
- else
- {
- /* Logical destination. Call match_logical_addr for each APIC. */
- if ( dest != 0 )
- {
- for ( i = 0; i < s->lapic_count; i++ )
+ if ( VLAPIC_ID(vcpu_vlapic(v)) == dest )
{
- if ( s->lapic_info[i] &&
- ioapic_match_logical_addr(s, i, dest) )
- mask |= (1<<i);
+ mask = 1 << v->vcpu_id;
+ break;
}
}
}
+ else if ( dest != 0 ) /* Logical mode, MDA non-zero. */
+ {
+ for_each_vcpu ( vioapic_domain(vioapic), v )
+ if ( vlapic_match_logical_addr(vcpu_vlapic(v), dest) )
+ mask |= 1 << v->vcpu_id;
+ }
+ out:
HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "ioapic_get_delivery_bitmask "
- "mask %x\n", mask);
-
+ "mask %x\n", mask);
return mask;
}
-static void ioapic_deliver(hvm_vioapic_t *s, int irqno)
+static void ioapic_deliver(struct vioapic *vioapic, int irq)
{
- uint16_t dest = s->redirtbl[irqno].RedirForm.dest_id;
- uint8_t dest_mode = s->redirtbl[irqno].RedirForm.destmode;
- uint8_t delivery_mode = s->redirtbl[irqno].RedirForm.deliver_mode;
- uint8_t vector = s->redirtbl[irqno].RedirForm.vector;
- uint8_t trig_mode = s->redirtbl[irqno].RedirForm.trigmod;
+ uint16_t dest = vioapic->redirtbl[irq].fields.dest_id;
+ uint8_t dest_mode = vioapic->redirtbl[irq].fields.dest_mode;
+ uint8_t delivery_mode = vioapic->redirtbl[irq].fields.delivery_mode;
+ uint8_t vector = vioapic->redirtbl[irq].fields.vector;
+ uint8_t trig_mode = vioapic->redirtbl[irq].fields.trig_mode;
uint32_t deliver_bitmask;
struct vlapic *target;
-
+ struct vcpu *v;
HVM_DBG_LOG(DBG_LEVEL_IOAPIC,
- "dest %x dest_mode %x delivery_mode %x vector %x trig_mode %x\n",
- dest, dest_mode, delivery_mode, vector, trig_mode);
+ "dest=%x dest_mode=%x delivery_mode=%x "
+ "vector=%x trig_mode=%x\n",
+ dest, dest_mode, delivery_mode, vector, trig_mode);
deliver_bitmask = ioapic_get_delivery_bitmask(
- s, dest, dest_mode, vector, delivery_mode);
-
- if (!deliver_bitmask) {
+ vioapic, dest, dest_mode, vector, delivery_mode);
+ if ( !deliver_bitmask )
+ {
HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "ioapic deliver "
- "no target on destination\n");
-
+ "no target on destination\n");
return;
}
- switch (delivery_mode) {
+ switch ( delivery_mode )
+ {
case dest_LowestPrio:
{
#ifdef IRQ0_SPECIAL_ROUTING
/* Force round-robin to pick VCPU 0 */
- if (irqno == 0)
- target = s->lapic_info[0];
+ if ( irq == 0 )
+ {
+ v = vioapic_domain(vioapic)->vcpu[0];
+ target = v ? vcpu_vlapic(v) : NULL;
+ }
else
#endif
- target = apic_round_robin(s->domain, dest_mode,
+ target = apic_round_robin(vioapic_domain(vioapic),
vector, deliver_bitmask);
- if (target)
- ioapic_inj_irq(s, target, vector, trig_mode, delivery_mode);
+ if ( target != NULL )
+ {
+ ioapic_inj_irq(vioapic, target, vector, trig_mode, delivery_mode);
+ vcpu_kick(vlapic_vcpu(target));
+ }
else
- HVM_DBG_LOG(DBG_LEVEL_IOAPIC,
- "null round robin mask %x vector %x delivery_mode %x\n",
- deliver_bitmask, vector, dest_LowestPrio);
+ {
+ HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "null round robin: "
+ "mask=%x vector=%x delivery_mode=%x\n",
+ deliver_bitmask, vector, dest_LowestPrio);
+ }
break;
}
@@ -468,18 +423,25 @@ static void ioapic_deliver(hvm_vioapic_t *s, int irqno)
case dest_ExtINT:
{
uint8_t bit;
- for (bit = 0; bit < s->lapic_count; bit++) {
+ for ( bit = 0; deliver_bitmask != 0; bit++ )
+ {
if ( !(deliver_bitmask & (1 << bit)) )
continue;
+ deliver_bitmask &= ~(1 << bit);
#ifdef IRQ0_SPECIAL_ROUTING
/* Do not deliver timer interrupts to VCPU != 0 */
- if ( (irqno == 0) && (bit !=0 ) )
- target = s->lapic_info[0];
+ if ( (irq == 0) && (bit != 0) )
+ v = vioapic_domain(vioapic)->vcpu[0];
else
#endif
- target = s->lapic_info[bit];
- if (target)
- ioapic_inj_irq(s, target, vector, trig_mode, delivery_mode);
+ v = vioapic_domain(vioapic)->vcpu[bit];
+ if ( v != NULL )
+ {
+ target = vcpu_vlapic(v);
+ ioapic_inj_irq(vioapic, target, vector,
+ trig_mode, delivery_mode);
+ vcpu_kick(vlapic_vcpu(target));
+ }
}
break;
}
@@ -489,194 +451,140 @@ static void ioapic_deliver(hvm_vioapic_t *s, int irqno)
case dest_INIT:
case dest__reserved_2:
default:
- printk("Not support delivey mode %d\n", delivery_mode);
+ gdprintk(XENLOG_WARNING, "Unsupported delivery mode %d\n",
+ delivery_mode);
break;
}
}
-static int ioapic_get_highest_irq(hvm_vioapic_t *s)
+static int ioapic_get_highest_irq(struct vioapic *vioapic)
{
- uint32_t irqs = (s->irr | s->irr_xen) & ~s->isr & ~s->imr;
+ uint32_t irqs = vioapic->irr | vioapic->irr_xen;
+ irqs &= ~vioapic->isr & ~vioapic->imr;
return fls(irqs) - 1;
}
-static void service_ioapic(hvm_vioapic_t *s)
+static void service_ioapic(struct vioapic *vioapic)
{
- int irqno;
+ int irq;
- while ((irqno = ioapic_get_highest_irq(s)) != -1) {
+ while ( (irq = ioapic_get_highest_irq(vioapic)) != -1 )
+ {
+ HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "service_ioapic highest irq %x\n", irq);
- HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "service_ioapic "
- "highest irqno %x\n", irqno);
+ if ( !test_bit(irq, &vioapic->imr) )
+ ioapic_deliver(vioapic, irq);
- if (!test_bit(irqno, &s->imr)) {
- ioapic_deliver(s, irqno);
- }
+ if ( vioapic->redirtbl[irq].fields.trig_mode == VIOAPIC_LEVEL_TRIG )
+ vioapic->isr |= (1 << irq);
- if (s->redirtbl[irqno].RedirForm.trigmod == IOAPIC_LEVEL_TRIGGER) {
- s->isr |= (1 << irqno);
- }
-
- s->irr &= ~(1 << irqno);
- s->irr_xen &= ~(1 << irqno);
+ vioapic->irr &= ~(1 << irq);
+ vioapic->irr_xen &= ~(1 << irq);
}
}
-void hvm_vioapic_do_irqs(struct domain *d, uint16_t irqs)
+void vioapic_set_xen_irq(struct domain *d, int irq, int level)
{
- hvm_vioapic_t *s = &(d->arch.hvm_domain.vioapic);
+ struct vioapic *vioapic = domain_vioapic(d);
- if (!hvm_apic_support(d))
+ if ( vioapic->redirtbl[irq].fields.mask )
return;
- s->irr |= irqs & ~s->imr;
- service_ioapic(s);
-}
-
-void hvm_vioapic_do_irqs_clear(struct domain *d, uint16_t irqs)
-{
- hvm_vioapic_t *s = &(d->arch.hvm_domain.vioapic);
-
- if (!hvm_apic_support(d))
- return;
+ if ( vioapic->redirtbl[irq].fields.trig_mode == VIOAPIC_EDGE_TRIG )
+ gdprintk(XENLOG_WARNING, "Forcing edge triggered APIC irq %d?\n", irq);
- s->irr &= ~irqs;
- service_ioapic(s);
-}
-
-void hvm_vioapic_set_xen_irq(struct domain *d, int irq, int level)
-{
- hvm_vioapic_t *s = &d->arch.hvm_domain.vioapic;
-
- if (!hvm_apic_support(d) || !IOAPICEnabled(s) ||
- s->redirtbl[irq].RedirForm.mask)
- return;
-
- if (s->redirtbl[irq].RedirForm.trigmod != IOAPIC_LEVEL_TRIGGER) {
- DPRINTK("Forcing edge triggered APIC irq %d?\n", irq);
- domain_crash(d);
- }
-
- if (level)
- s->irr_xen |= 1 << irq;
+ if ( level )
+ vioapic->irr_xen |= 1 << irq;
else
- s->irr_xen &= ~(1 << irq);
+ vioapic->irr_xen &= ~(1 << irq);
+
+ service_ioapic(vioapic);
}
-void hvm_vioapic_set_irq(struct domain *d, int irq, int level)
+void vioapic_set_irq(struct domain *d, int irq, int level)
{
- hvm_vioapic_t *s = &(d->arch.hvm_domain.vioapic);
-
- if (!hvm_apic_support(d))
- return ;
+ struct vioapic *vioapic = domain_vioapic(d);
+ uint32_t bit;
HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "ioapic_set_irq "
- "irq %x level %x\n", irq, level);
+ "irq %x level %x\n", irq, level);
- if (irq < 0 || irq >= IOAPIC_NUM_PINS) {
- printk("ioapic_set_irq irq %x is illegal\n", irq);
- domain_crash_synchronous();
- }
+ if ( (irq < 0) || (irq >= VIOAPIC_NUM_PINS) )
+ return;
- if (!IOAPICEnabled(s) || s->redirtbl[irq].RedirForm.mask)
+ if ( vioapic->redirtbl[irq].fields.mask )
return;
- HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "hvm_vioapic_set_irq entry %x "
- "vector %x deliver_mod %x destmode %x delivestatus %x "
- "polarity %x remote_irr %x trigmod %x mask %x dest_id %x\n",
- irq,
- s->redirtbl[irq].RedirForm.vector,
- s->redirtbl[irq].RedirForm.deliver_mode,
- s->redirtbl[irq].RedirForm.destmode,
- s->redirtbl[irq].RedirForm.delivestatus,
- s->redirtbl[irq].RedirForm.polarity,
- s->redirtbl[irq].RedirForm.remoteirr,
- s->redirtbl[irq].RedirForm.trigmod,
- s->redirtbl[irq].RedirForm.mask,
- s->redirtbl[irq].RedirForm.dest_id);
-
- if (irq >= 0 && irq < IOAPIC_NUM_PINS) {
- uint32_t bit = 1 << irq;
- if (s->redirtbl[irq].RedirForm.trigmod == IOAPIC_LEVEL_TRIGGER) {
- if (level)
- s->irr |= bit;
- else
- s->irr &= ~bit;
- } else {
- if (level)
- /* XXX No irr clear for edge interrupt */
- s->irr |= bit;
- }
+ HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "vioapic_set_irq entry %x "
+ "vector %x delivery_mode %x dest_mode %x delivery_status %x "
+ "polarity %x remote_irr %x trig_mode %x mask %x dest_id %x\n",
+ irq,
+ vioapic->redirtbl[irq].fields.vector,
+ vioapic->redirtbl[irq].fields.delivery_mode,
+ vioapic->redirtbl[irq].fields.dest_mode,
+ vioapic->redirtbl[irq].fields.delivery_status,
+ vioapic->redirtbl[irq].fields.polarity,
+ vioapic->redirtbl[irq].fields.remote_irr,
+ vioapic->redirtbl[irq].fields.trig_mode,
+ vioapic->redirtbl[irq].fields.mask,
+ vioapic->redirtbl[irq].fields.dest_id);
+
+ bit = 1 << irq;
+ if ( vioapic->redirtbl[irq].fields.trig_mode == VIOAPIC_LEVEL_TRIG )
+ {
+ if ( level )
+ vioapic->irr |= bit;
+ else
+ vioapic->irr &= ~bit;
+ }
+ else
+ {
+ if ( level )
+ /* XXX No irr clear for edge interrupt */
+ vioapic->irr |= bit;
}
- service_ioapic(s);
+ service_ioapic(vioapic);
}
/* XXX If level interrupt, use vector->irq table for performance */
-static int get_redir_num(hvm_vioapic_t *s, int vector)
+static int get_redir_num(struct vioapic *vioapic, int vector)
{
- int i = 0;
-
- ASSERT(s);
+ int i;
- for(i = 0; i < IOAPIC_NUM_PINS; i++) {
- if (s->redirtbl[i].RedirForm.vector == vector)
+ for ( i = 0; i < VIOAPIC_NUM_PINS; i++ )
+ if ( vioapic->redirtbl[i].fields.vector == vector )
return i;
- }
return -1;
}
-void ioapic_update_EOI(struct domain *d, int vector)
+void vioapic_update_EOI(struct domain *d, int vector)
{
- hvm_vioapic_t *s = &(d->arch.hvm_domain.vioapic);
+ struct vioapic *vioapic = domain_vioapic(d);
int redir_num;
- if ((redir_num = get_redir_num(s, vector)) == -1) {
- printk("Can't find redir item for %d EOI \n", vector);
+ if ( (redir_num = get_redir_num(vioapic, vector)) == -1 )
+ {
+ gdprintk(XENLOG_WARNING, "Can't find redir item for %d EOI\n", vector);
return;
}
- if (!test_and_clear_bit(redir_num, &s->isr)) {
- printk("redir %d not set for %d EOI\n", redir_num, vector);
+ if ( !test_and_clear_bit(redir_num, &vioapic->isr) )
+ {
+ gdprintk(XENLOG_WARNING, "redir %d not set for %d EOI\n",
+ redir_num, vector);
return;
}
}
-int hvm_vioapic_add_lapic(struct vlapic *vlapic, struct vcpu *v)
-{
- hvm_vioapic_t *s = &(v->domain->arch.hvm_domain.vioapic);
-
- if (v->vcpu_id != s->lapic_count) {
- printk("hvm_vioapic_add_lapic "
- "cpu_id not match vcpu_id %x lapic_count %x\n",
- v->vcpu_id, s->lapic_count);
- domain_crash_synchronous();
- }
-
- /* update count later for race condition on interrupt */
- s->lapic_info[s->lapic_count] = vlapic;
- s->lapic_count ++;
-
- return s->lapic_count;
-}
-
-hvm_vioapic_t * hvm_vioapic_init(struct domain *d)
+void vioapic_init(struct domain *d)
{
- int i = 0;
- hvm_vioapic_t *s = &(d->arch.hvm_domain.vioapic);
-
- HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "hvm_vioapic_init\n");
-
- hvm_vioapic_reset(s);
-
- s->domain = d;
+ struct vioapic *vioapic = domain_vioapic(d);
- for (i = 0; i < MAX_LAPIC_NUM; i++)
- s->lapic_info[i] = NULL;
+ HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "vioapic_init\n");
- /* Remove after GFW ready */
- ioapic_update_config(s, IOAPIC_DEFAULT_BASE_ADDRESS, 1);
+ vioapic_reset(vioapic);
- return s;
+ vioapic->base_address = VIOAPIC_DEFAULT_BASE_ADDRESS;
}
diff --git a/xen/arch/x86/hvm/vlapic.c b/xen/arch/x86/hvm/vlapic.c
index 8c121ee2c3..3209416157 100644
--- a/xen/arch/x86/hvm/vlapic.c
+++ b/xen/arch/x86/hvm/vlapic.c
@@ -29,13 +29,15 @@
#include <asm/hvm/hvm.h>
#include <asm/hvm/io.h>
#include <asm/hvm/support.h>
-
#include <xen/lib.h>
#include <xen/sched.h>
#include <asm/current.h>
#include <public/hvm/ioreq.h>
#include <public/hvm/params.h>
+#define VLAPIC_VERSION 0x00050014
+#define VLAPIC_LVT_NUM 6
+
/* XXX remove this definition after GFW enabled */
#define VLAPIC_NO_BIOS
@@ -43,6 +45,13 @@ extern u32 get_apic_bus_cycle(void);
#define APIC_BUS_CYCLE_NS (((s_time_t)get_apic_bus_cycle()) / 1000)
+#define LVT_MASK \
+ APIC_LVT_MASKED | APIC_SEND_PENDING | APIC_VECTOR_MASK
+
+#define LINT_MASK \
+ LVT_MASK | APIC_MODE_MASK | APIC_INPUT_POLARITY |\
+ APIC_LVT_REMOTE_IRR | APIC_LVT_LEVEL_TRIGGER
+
static unsigned int vlapic_lvt_mask[VLAPIC_LVT_NUM] =
{
/* LVTT */
@@ -57,9 +66,70 @@ static unsigned int vlapic_lvt_mask[VLAPIC_LVT_NUM] =
LVT_MASK
};
-int hvm_apic_support(struct domain *d)
+/* Following could belong in apicdef.h */
+#define APIC_SHORT_MASK 0xc0000
+#define APIC_DEST_NOSHORT 0x0
+#define APIC_DEST_MASK 0x800
+
+#define vlapic_lvt_enabled(vlapic, lvt_type) \
+ (!(vlapic_get_reg(vlapic, lvt_type) & APIC_LVT_MASKED))
+
+#define vlapic_lvt_vector(vlapic, lvt_type) \
+ (vlapic_get_reg(vlapic, lvt_type) & APIC_VECTOR_MASK)
+
+#define vlapic_lvt_dm(vlapic, lvt_type) \
+ (vlapic_get_reg(vlapic, lvt_type) & APIC_MODE_MASK)
+
+#define vlapic_lvtt_period(vlapic) \
+ (vlapic_get_reg(vlapic, APIC_LVTT) & APIC_LVT_TIMER_PERIODIC)
+
+/*
+ * Generic APIC bitmap vector update & search routines.
+ */
+
+#define VEC_POS(v) ((v)%32)
+#define REG_POS(v) (((v)/32)* 0x10)
+#define vlapic_test_and_set_vector(vec, bitmap) \
+ test_and_set_bit(VEC_POS(vec), (bitmap) + REG_POS(vec))
+#define vlapic_test_and_clear_vector(vec, bitmap) \
+ test_and_clear_bit(VEC_POS(vec), (bitmap) + REG_POS(vec))
+#define vlapic_set_vector(vec, bitmap) \
+ set_bit(VEC_POS(vec), (bitmap) + REG_POS(vec))
+#define vlapic_clear_vector(vec, bitmap) \
+ clear_bit(VEC_POS(vec), (bitmap) + REG_POS(vec))
+
+static int vlapic_find_highest_vector(u32 *bitmap)
+{
+ int word_offset = MAX_VECTOR / 32;
+
+ /* Work backwards through the bitmap (first 32-bit word in every four). */
+ while ( (word_offset != 0) && (bitmap[(--word_offset)*4] == 0) )
+ continue;
+
+ return (fls(bitmap[word_offset*4]) - 1) + (word_offset * 32);
+}
+
+
+/*
+ * IRR-specific bitmap update & search routines.
+ */
+
+static int vlapic_test_and_set_irr(int vector, struct vlapic *vlapic)
+{
+ vlapic->flush_tpr_threshold = 1;
+ return vlapic_test_and_set_vector(vector, vlapic->regs + APIC_IRR);
+}
+
+static void vlapic_set_irr(int vector, struct vlapic *vlapic)
+{
+ vlapic->flush_tpr_threshold = 1;
+ vlapic_set_vector(vector, vlapic->regs + APIC_IRR);
+}
+
+static void vlapic_clear_irr(int vector, struct vlapic *vlapic)
{
- return d->arch.hvm_domain.params[HVM_PARAM_APIC_ENABLED];
+ vlapic->flush_tpr_threshold = 1;
+ vlapic_clear_vector(vector, vlapic->regs + APIC_IRR);
}
int vlapic_find_highest_irr(struct vlapic *vlapic)
@@ -72,12 +142,24 @@ int vlapic_find_highest_irr(struct vlapic *vlapic)
return result;
}
+
+int vlapic_set_irq(struct vlapic *vlapic, uint8_t vec, uint8_t trig)
+{
+ int ret;
+
+ ret = vlapic_test_and_set_irr(vec, vlapic);
+ if ( trig )
+ vlapic_set_vector(vec, vlapic->regs + APIC_TMR);
+
+ /* We may need to wake up target vcpu, besides set pending bit here */
+ return ret;
+}
+
s_time_t get_apictime_scheduled(struct vcpu *v)
{
- struct vlapic *vlapic = VLAPIC(v);
+ struct vlapic *vlapic = vcpu_vlapic(v);
- if ( !hvm_apic_support(v->domain) ||
- !vlapic_lvt_enabled(vlapic, APIC_LVTT) )
+ if ( !vlapic_lvt_enabled(vlapic, APIC_LVTT) )
return -1;
return vlapic->vlapic_timer.expires;
@@ -93,26 +175,19 @@ int vlapic_find_highest_isr(struct vlapic *vlapic)
return result;
}
-uint32_t vlapic_update_ppr(struct vlapic *vlapic)
+uint32_t vlapic_get_ppr(struct vlapic *vlapic)
{
uint32_t tpr, isrv, ppr;
int isr;
- tpr = vlapic_get_reg(vlapic, APIC_TASKPRI);
-
- isr = vlapic_find_highest_isr(vlapic);
+ tpr = vlapic_get_reg(vlapic, APIC_TASKPRI);
+ isr = vlapic_find_highest_isr(vlapic);
+ isrv = (isr != -1) ? isr : 0;
- if ( isr != -1 )
- isrv = (isr >> 4) & 0xf; /* ditto */
- else
- isrv = 0;
-
- if ( (tpr >> 4) >= isrv )
+ if ( (tpr & 0xf0) >= (isrv & 0xf0) )
ppr = tpr & 0xff;
else
- ppr = isrv << 4; /* low 4 bits of PPR have to be cleared */
-
- vlapic_set_reg(vlapic, APIC_PROCPRI, ppr);
+ ppr = isrv & 0xf0;
HVM_DBG_LOG(DBG_LEVEL_VLAPIC_INTERRUPT,
"vlapic %p, ppr 0x%x, isr 0x%x, isrv 0x%x.",
@@ -127,7 +202,7 @@ static int vlapic_match_dest(struct vcpu *v, struct vlapic *source,
int delivery_mode)
{
int result = 0;
- struct vlapic *target = VLAPIC(v);
+ struct vlapic *target = vcpu_vlapic(v);
HVM_DBG_LOG(DBG_LEVEL_VLAPIC, "target %p, source %p, dest 0x%x, "
"dest_mode 0x%x, short_hand 0x%x, delivery_mode 0x%x.",
@@ -139,11 +214,13 @@ static int vlapic_match_dest(struct vcpu *v, struct vlapic *source,
(delivery_mode != APIC_DM_NMI)) )
{
HVM_DBG_LOG(DBG_LEVEL_VLAPIC, "uninitialized target vcpu %p, "
- "delivery_mode 0x%x, dest 0x%x.\n", v, delivery_mode, dest);
+ "delivery_mode 0x%x, dest 0x%x.\n",
+ v, delivery_mode, dest);
return result;
}
- switch ( short_hand ) {
+ switch ( short_hand )
+ {
case APIC_DEST_NOSHORT: /* no shorthand */
if ( !dest_mode ) /* Physical */
{
@@ -159,7 +236,7 @@ static int vlapic_match_dest(struct vcpu *v, struct vlapic *source,
ldr = vlapic_get_reg(target, APIC_LDR);
/* Flat mode */
- if ( vlapic_get_reg(target, APIC_DFR) == APIC_DFR_FLAT)
+ if ( vlapic_get_reg(target, APIC_DFR) == APIC_DFR_FLAT )
{
result = GET_APIC_LOGICAL_ID(ldr) & dest;
}
@@ -169,12 +246,12 @@ static int vlapic_match_dest(struct vcpu *v, struct vlapic *source,
(dest == 0xff) )
{
/* What shall we do now? */
- printk("Broadcast IPI with lowest priority "
- "delivery mode\n");
+ gdprintk(XENLOG_ERR, "Broadcast IPI with lowest priority "
+ "delivery mode\n");
domain_crash_synchronous();
}
- result = (GET_APIC_LOGICAL_ID(ldr) == (dest & 0xf)) ?
- (GET_APIC_LOGICAL_ID(ldr) >> 4) & (dest >> 4) : 0;
+ result = ((GET_APIC_LOGICAL_ID(ldr) == (dest & 0xf)) ?
+ (GET_APIC_LOGICAL_ID(ldr) >> 4) & (dest >> 4) : 0);
}
}
break;
@@ -208,27 +285,27 @@ static int vlapic_accept_irq(struct vcpu *v, int delivery_mode,
int vector, int level, int trig_mode)
{
int result = 0;
- struct vlapic *vlapic = VLAPIC(v);
+ struct vlapic *vlapic = vcpu_vlapic(v);
- switch ( delivery_mode ) {
+ switch ( delivery_mode )
+ {
case APIC_DM_FIXED:
case APIC_DM_LOWEST:
/* FIXME add logic for vcpu on reset */
if ( unlikely(vlapic == NULL || !vlapic_enabled(vlapic)) )
break;
- if ( vlapic_test_and_set_vector(vector, vlapic->regs + APIC_IRR) &&
- trig_mode)
+ if ( vlapic_test_and_set_irr(vector, vlapic) && trig_mode )
{
HVM_DBG_LOG(DBG_LEVEL_VLAPIC,
- "level trig mode repeatedly for vector %d\n", vector);
+ "level trig mode repeatedly for vector %d\n", vector);
break;
}
if ( trig_mode )
{
HVM_DBG_LOG(DBG_LEVEL_VLAPIC,
- "level trig mode for vector %d\n", vector);
+ "level trig mode for vector %d\n", vector);
vlapic_set_vector(vector, vlapic->regs + APIC_TMR);
}
@@ -238,43 +315,40 @@ static int vlapic_accept_irq(struct vcpu *v, int delivery_mode,
break;
case APIC_DM_REMRD:
- printk("Ignore deliver mode 3 in vlapic_accept_irq\n");
+ gdprintk(XENLOG_WARNING, "Ignoring delivery mode 3\n");
break;
case APIC_DM_SMI:
case APIC_DM_NMI:
- /* Fixme */
- printk("TODO: for guest SMI/NMI\n");
+ gdprintk(XENLOG_WARNING, "Ignoring guest SMI/NMI\n");
break;
case APIC_DM_INIT:
- if ( trig_mode && !(level & APIC_INT_ASSERT) ) //Deassert
- printk("This hvm_vlapic is for P4, no work for De-assert init\n");
- else
+ /* No work on INIT de-assert for P4-type APIC. */
+ if ( trig_mode && !(level & APIC_INT_ASSERT) )
+ break;
+ /* FIXME How to check the situation after vcpu reset? */
+ if ( test_and_clear_bit(_VCPUF_initialised, &v->vcpu_flags) )
{
- /* FIXME How to check the situation after vcpu reset? */
- if ( test_and_clear_bit(_VCPUF_initialised, &v->vcpu_flags) )
- {
- printk("Reset hvm vcpu not supported yet\n");
- domain_crash_synchronous();
- }
- v->arch.hvm_vcpu.init_sipi_sipi_state =
- HVM_VCPU_INIT_SIPI_SIPI_STATE_WAIT_SIPI;
- result = 1;
+ gdprintk(XENLOG_ERR, "Reset hvm vcpu not supported yet\n");
+ domain_crash_synchronous();
}
+ v->arch.hvm_vcpu.init_sipi_sipi_state =
+ HVM_VCPU_INIT_SIPI_SIPI_STATE_WAIT_SIPI;
+ result = 1;
break;
case APIC_DM_STARTUP:
if ( v->arch.hvm_vcpu.init_sipi_sipi_state ==
- HVM_VCPU_INIT_SIPI_SIPI_STATE_NORM )
+ HVM_VCPU_INIT_SIPI_SIPI_STATE_NORM )
break;
v->arch.hvm_vcpu.init_sipi_sipi_state =
- HVM_VCPU_INIT_SIPI_SIPI_STATE_NORM;
+ HVM_VCPU_INIT_SIPI_SIPI_STATE_NORM;
if ( test_bit(_VCPUF_initialised, &v->vcpu_flags) )
{
- printk("SIPI for initialized vcpu vcpuid %x\n", v->vcpu_id);
+ gdprintk(XENLOG_ERR, "SIPI for initialized vcpu %x\n", v->vcpu_id);
domain_crash_synchronous();
}
@@ -283,7 +357,8 @@ static int vlapic_accept_irq(struct vcpu *v, int delivery_mode,
break;
default:
- printk("TODO: not support interrupt type %x\n", delivery_mode);
+ gdprintk(XENLOG_ERR, "TODO: unsupported delivery mode %x\n",
+ delivery_mode);
domain_crash_synchronous();
break;
}
@@ -291,53 +366,31 @@ static int vlapic_accept_irq(struct vcpu *v, int delivery_mode,
return result;
}
-/*
- * This function is used by both ioapic and local APIC
- * The bitmap is for vcpu_id
- */
-struct vlapic *apic_round_robin(struct domain *d,
- uint8_t dest_mode,
- uint8_t vector,
- uint32_t bitmap)
+/* This function is used by both ioapic and lapic.The bitmap is for vcpu_id. */
+struct vlapic *apic_round_robin(
+ struct domain *d, uint8_t vector, uint32_t bitmap)
{
int next, old;
- struct vlapic* target = NULL;
-
- if ( dest_mode == 0 ) //Physical mode
- {
- printk("<apic_round_robin> lowest priority for physical mode.\n");
- return NULL;
- }
-
- if ( !bitmap )
- {
- printk("<apic_round_robin> no bit set in bitmap.\n");
- return NULL;
- }
+ struct vlapic *target = NULL;
spin_lock(&d->arch.hvm_domain.round_robin_lock);
old = next = d->arch.hvm_domain.round_info[vector];
/* the vcpu array is arranged according to vcpu_id */
- do
- {
+ do {
if ( ++next == MAX_VIRT_CPUS )
next = 0;
- if ( d->vcpu[next] == NULL ||
+ if ( (d->vcpu[next] == NULL) ||
!test_bit(_VCPUF_initialised, &d->vcpu[next]->vcpu_flags) )
continue;
if ( test_bit(next, &bitmap) )
{
- target = d->vcpu[next]->arch.hvm_vcpu.vlapic;
-
- if ( target == NULL || !vlapic_enabled(target) )
- {
- printk("warning: targe round robin local apic disabled\n");
- /* XXX should we domain crash?? Or should we return NULL */
- }
- break;
+ target = vcpu_vlapic(d->vcpu[next]);
+ if ( vlapic_enabled(target) )
+ break;
+ target = NULL;
}
} while ( next != old );
@@ -357,25 +410,9 @@ void vlapic_EOI_set(struct vlapic *vlapic)
return ;
vlapic_clear_vector(vector, vlapic->regs + APIC_ISR);
- vlapic_update_ppr(vlapic);
if ( vlapic_test_and_clear_vector(vector, vlapic->regs + APIC_TMR) )
- ioapic_update_EOI(vlapic->domain, vector);
-}
-
-static int vlapic_check_vector(struct vlapic *vlapic,
- uint32_t dm, uint32_t vector)
-{
- if ( (dm == APIC_DM_FIXED) && (vector < 16) )
- {
- vlapic->err_status |= 0x40;
- vlapic_accept_irq(vlapic->vcpu, APIC_DM_FIXED,
- vlapic_lvt_vector(vlapic, APIC_LVTERR), 0, 0);
- printk("<vlapic_check_vector>: check failed "
- " dm %x vector %x\n", dm, vector);
- return 0;
- }
- return 1;
+ vioapic_update_EOI(vlapic_domain(vlapic), vector);
}
static void vlapic_ipi(struct vlapic *vlapic)
@@ -388,12 +425,12 @@ static void vlapic_ipi(struct vlapic *vlapic)
unsigned int trig_mode = icr_low & APIC_INT_LEVELTRIG;
unsigned int level = icr_low & APIC_INT_ASSERT;
unsigned int dest_mode = icr_low & APIC_DEST_MASK;
- unsigned int delivery_mode = icr_low & APIC_MODE_MASK;
+ unsigned int delivery_mode =icr_low & APIC_MODE_MASK;
unsigned int vector = icr_low & APIC_VECTOR_MASK;
struct vlapic *target;
- struct vcpu *v = NULL;
- uint32_t lpr_map=0;
+ struct vcpu *v;
+ uint32_t lpr_map = 0;
HVM_DBG_LOG(DBG_LEVEL_VLAPIC, "icr_high 0x%x, icr_low 0x%x, "
"short_hand 0x%x, dest 0x%x, trig_mode 0x%x, level 0x%x, "
@@ -401,7 +438,7 @@ static void vlapic_ipi(struct vlapic *vlapic)
icr_high, icr_low, short_hand, dest,
trig_mode, level, dest_mode, delivery_mode, vector);
- for_each_vcpu ( vlapic->domain, v )
+ for_each_vcpu ( vlapic_domain(vlapic), v )
{
if ( vlapic_match_dest(v, vlapic, short_hand,
dest, dest_mode, delivery_mode) )
@@ -414,13 +451,11 @@ static void vlapic_ipi(struct vlapic *vlapic)
}
}
- if ( delivery_mode == APIC_DM_LOWEST)
+ if ( delivery_mode == APIC_DM_LOWEST )
{
- v = vlapic->vcpu;
- target = apic_round_robin(v->domain, dest_mode, vector, lpr_map);
-
- if ( target )
- vlapic_accept_irq(target->vcpu, delivery_mode,
+ target = apic_round_robin(vlapic_domain(v), vector, lpr_map);
+ if ( target != NULL )
+ vlapic_accept_irq(vlapic_vcpu(target), delivery_mode,
vector, level, trig_mode);
}
}
@@ -431,8 +466,6 @@ static uint32_t vlapic_get_tmcct(struct vlapic *vlapic)
s_time_t passed, now = NOW();
uint32_t tmcct = vlapic_get_reg(vlapic, APIC_TMCCT);
- ASSERT(vlapic != NULL);
-
if ( unlikely(now <= vlapic->timer_last_update) )
{
passed = ~0x0LL - vlapic->timer_last_update + now;
@@ -441,8 +474,7 @@ static uint32_t vlapic_get_tmcct(struct vlapic *vlapic)
else
passed = now - vlapic->timer_last_update;
- counter_passed = passed /
- (APIC_BUS_CYCLE_NS * vlapic->timer_divide_count);
+ counter_passed = passed / (APIC_BUS_CYCLE_NS * vlapic->timer_divisor);
tmcct -= counter_passed;
@@ -451,7 +483,7 @@ static uint32_t vlapic_get_tmcct(struct vlapic *vlapic)
if ( unlikely(!vlapic_lvtt_period(vlapic)) )
{
tmcct = 0;
- // FIXME: should we add interrupt here?
+ /* FIXME: should we add interrupt here? */
}
else
{
@@ -474,27 +506,32 @@ static uint32_t vlapic_get_tmcct(struct vlapic *vlapic)
return tmcct;
}
+static void vlapic_set_tdcr(struct vlapic *vlapic, unsigned int val)
+{
+ /* Only bits 0, 1 and 3 are settable; others are MBZ. */
+ val &= 0xb;
+ vlapic_set_reg(vlapic, APIC_TDCR, val);
+
+ /* Update the demangled timer_divisor. */
+ val = ((val & 3) | ((val & 8) >> 1)) + 1;
+ vlapic->timer_divisor = 1 << (val & 7);
+}
+
static void vlapic_read_aligned(struct vlapic *vlapic, unsigned int offset,
unsigned int len, unsigned int *result)
{
- ASSERT(len == 4 && offset > 0 && offset <= APIC_TDCR);
-
- *result = 0;
+ ASSERT((len == 4) && (offset > 0) && (offset <= APIC_TDCR));
- switch ( offset ) {
- case APIC_ARBPRI:
- printk("access local APIC ARBPRI register which is for P6\n");
+ switch ( offset )
+ {
+ case APIC_PROCPRI:
+ *result = vlapic_get_ppr(vlapic);
break;
- case APIC_TMCCT: //Timer CCR
+ case APIC_TMCCT: /* Timer CCR */
*result = vlapic_get_tmcct(vlapic);
break;
- case APIC_ESR:
- vlapic->err_write_count = 0;
- *result = vlapic_get_reg(vlapic, offset);
- break;
-
default:
*result = vlapic_get_reg(vlapic, offset);
break;
@@ -507,10 +544,10 @@ static unsigned long vlapic_read(struct vcpu *v, unsigned long address,
unsigned int alignment;
unsigned int tmp;
unsigned long result;
- struct vlapic *vlapic = VLAPIC(v);
+ struct vlapic *vlapic = vcpu_vlapic(v);
unsigned int offset = address - vlapic->base_address;
- if ( offset > APIC_TDCR)
+ if ( offset > APIC_TDCR )
return 0;
/* some bugs on kernel cause read this with byte*/
@@ -522,7 +559,8 @@ static unsigned long vlapic_read(struct vcpu *v, unsigned long address,
alignment = offset & 0x3;
vlapic_read_aligned(vlapic, offset & ~0x3, 4, &tmp);
- switch ( len ) {
+ switch ( len )
+ {
case 1:
result = *((unsigned char *)&tmp + alignment);
break;
@@ -538,7 +576,8 @@ static unsigned long vlapic_read(struct vcpu *v, unsigned long address,
break;
default:
- printk("Local APIC read with len=0x%lx, should be 4 instead.\n", len);
+ gdprintk(XENLOG_ERR, "Local APIC read with len=0x%lx, "
+ "should be 4 instead.\n", len);
domain_crash_synchronous();
break;
}
@@ -552,7 +591,7 @@ static unsigned long vlapic_read(struct vcpu *v, unsigned long address,
static void vlapic_write(struct vcpu *v, unsigned long address,
unsigned long len, unsigned long val)
{
- struct vlapic *vlapic = VLAPIC(v);
+ struct vlapic *vlapic = vcpu_vlapic(v);
unsigned int offset = address - vlapic->base_address;
if ( offset != 0xb0 )
@@ -561,31 +600,31 @@ static void vlapic_write(struct vcpu *v, unsigned long address,
offset, len, val);
/*
- * According to IA 32 Manual, all resgiters should be accessed with
- * 32 bits alignment.
+ * According to the IA32 Manual, all accesses should be 32 bits.
+ * Some OSes do 8- or 16-byte accesses, however.
*/
if ( len != 4 )
{
unsigned int tmp;
unsigned char alignment;
- /* Some kernels do will access with byte/word alignment */
- printk("Notice: Local APIC write with len = %lx\n",len);
+ gdprintk(XENLOG_INFO, "Notice: Local APIC write with len = %lx\n",len);
+
alignment = offset & 0x3;
tmp = vlapic_read(v, offset & ~0x3, 4);
- switch ( len ) {
+
+ switch ( len )
+ {
case 1:
- /* XXX the saddr is a tmp variable from caller, so should be ok
- But we should still change the following ref to val to
- local variable later */
val = (tmp & ~(0xff << (8*alignment))) |
((val & 0xff) << (8*alignment));
break;
case 2:
- if ( alignment != 0x0 && alignment != 0x2 )
+ if ( alignment & 1 )
{
- printk("alignment error for vlapic with len == 2\n");
+ gdprintk(XENLOG_ERR, "Uneven alignment error for "
+ "2-byte vlapic access\n");
domain_crash_synchronous();
}
@@ -593,14 +632,9 @@ static void vlapic_write(struct vcpu *v, unsigned long address,
((val & 0xffff) << (8*alignment));
break;
- case 3:
- /* will it happen? */
- printk("vlapic_write with len = 3 !!!\n");
- domain_crash_synchronous();
- break;
-
default:
- printk("Local APIC write with len = %lx, should be 4 instead\n", len);
+ gdprintk(XENLOG_ERR, "Local APIC write with len = %lx, "
+ "should be 4 instead\n", len);
domain_crash_synchronous();
break;
}
@@ -608,14 +642,15 @@ static void vlapic_write(struct vcpu *v, unsigned long address,
offset &= 0xff0;
- switch ( offset ) {
+ switch ( offset )
+ {
case APIC_ID: /* Local APIC ID */
vlapic_set_reg(vlapic, APIC_ID, val);
break;
case APIC_TASKPRI:
vlapic_set_reg(vlapic, APIC_TASKPRI, val & 0xff);
- vlapic_update_ppr(vlapic);
+ vlapic->flush_tpr_threshold = 1;
break;
case APIC_EOI:
@@ -633,7 +668,7 @@ static void vlapic_write(struct vcpu *v, unsigned long address,
case APIC_SPIV:
vlapic_set_reg(vlapic, APIC_SPIV, val & 0x3ff);
- if ( !( val & APIC_SPIV_APIC_ENABLED) )
+ if ( !(val & APIC_SPIV_APIC_ENABLED) )
{
int i;
uint32_t lvt_val;
@@ -661,9 +696,7 @@ static void vlapic_write(struct vcpu *v, unsigned long address,
break;
case APIC_ESR:
- vlapic->err_write_count = !vlapic->err_write_count;
- if ( !vlapic->err_write_count )
- vlapic->err_status = 0;
+ /* Nothing to do. */
break;
case APIC_ICR:
@@ -676,95 +709,77 @@ static void vlapic_write(struct vcpu *v, unsigned long address,
vlapic_set_reg(vlapic, APIC_ICR2, val & 0xff000000);
break;
- case APIC_LVTT: // LVT Timer Reg
- case APIC_LVTTHMR: // LVT Thermal Monitor
- case APIC_LVTPC: // LVT Performance Counter
- case APIC_LVT0: // LVT LINT0 Reg
- case APIC_LVT1: // LVT Lint1 Reg
- case APIC_LVTERR: // LVT Error Reg
- {
- if ( vlapic->status & VLAPIC_SOFTWARE_DISABLE_MASK )
- val |= APIC_LVT_MASKED;
-
- val &= vlapic_lvt_mask[(offset - APIC_LVTT) >> 4];
+ case APIC_LVTT: /* LVT Timer Reg */
+ case APIC_LVTTHMR: /* LVT Thermal Monitor */
+ case APIC_LVTPC: /* LVT Performance Counter */
+ case APIC_LVT0: /* LVT LINT0 Reg */
+ case APIC_LVT1: /* LVT Lint1 Reg */
+ case APIC_LVTERR: /* LVT Error Reg */
+ {
+ if ( vlapic->status & VLAPIC_SOFTWARE_DISABLE_MASK )
+ val |= APIC_LVT_MASKED;
- vlapic_set_reg(vlapic, offset, val);
+ val &= vlapic_lvt_mask[(offset - APIC_LVTT) >> 4];
- /* On hardware, when write vector less than 0x20 will error */
- if ( !(val & APIC_LVT_MASKED) )
- vlapic_check_vector(vlapic, vlapic_lvt_dm(vlapic, offset),
- vlapic_lvt_vector(vlapic, offset));
+ vlapic_set_reg(vlapic, offset, val);
- if ( !vlapic->vcpu_id && (offset == APIC_LVT0) )
- {
- if ( (val & APIC_MODE_MASK) == APIC_DM_EXTINT )
- if ( val & APIC_LVT_MASKED)
- clear_bit(_VLAPIC_BSP_ACCEPT_PIC, &vlapic->status);
- else
- set_bit(_VLAPIC_BSP_ACCEPT_PIC, &vlapic->status);
- else
+ if ( (vlapic_vcpu(vlapic)->vcpu_id == 0) && (offset == APIC_LVT0) )
+ {
+ if ( (val & APIC_MODE_MASK) == APIC_DM_EXTINT )
+ if ( val & APIC_LVT_MASKED)
clear_bit(_VLAPIC_BSP_ACCEPT_PIC, &vlapic->status);
- }
-
+ else
+ set_bit(_VLAPIC_BSP_ACCEPT_PIC, &vlapic->status);
+ else
+ clear_bit(_VLAPIC_BSP_ACCEPT_PIC, &vlapic->status);
}
- break;
+ }
+ break;
case APIC_TMICT:
- {
- s_time_t now = NOW(), offset;
+ {
+ s_time_t now = NOW(), offset;
- stop_timer(&vlapic->vlapic_timer);
+ stop_timer(&vlapic->vlapic_timer);
- vlapic_set_reg(vlapic, APIC_TMICT, val);
- vlapic_set_reg(vlapic, APIC_TMCCT, val);
- vlapic->timer_last_update = now;
+ vlapic_set_reg(vlapic, APIC_TMICT, val);
+ vlapic_set_reg(vlapic, APIC_TMCCT, val);
+ vlapic->timer_last_update = now;
- offset = APIC_BUS_CYCLE_NS *
- vlapic->timer_divide_count * val;
+ offset = APIC_BUS_CYCLE_NS * vlapic->timer_divisor * val;
- set_timer(&vlapic->vlapic_timer, now + offset);
+ set_timer(&vlapic->vlapic_timer, now + offset);
- HVM_DBG_LOG(DBG_LEVEL_VLAPIC,
- "bus cycle is %"PRId64"ns, now 0x%016"PRIx64", "
- "timer initial count 0x%x, offset 0x%016"PRIx64", "
- "expire @ 0x%016"PRIx64".",
- APIC_BUS_CYCLE_NS, now,
- vlapic_get_reg(vlapic, APIC_TMICT),
- offset, now + offset);
- }
- break;
+ HVM_DBG_LOG(DBG_LEVEL_VLAPIC,
+ "bus cycle is %"PRId64"ns, now 0x%016"PRIx64", "
+ "timer initial count 0x%x, offset 0x%016"PRIx64", "
+ "expire @ 0x%016"PRIx64".",
+ APIC_BUS_CYCLE_NS, now,
+ vlapic_get_reg(vlapic, APIC_TMICT),
+ offset, now + offset);
+ }
+ break;
case APIC_TDCR:
- {
- unsigned int tmp1, tmp2;
-
- tmp1 = val & 0xf;
- tmp2 = ((tmp1 & 0x3) | ((tmp1 & 0x8) >> 1)) + 1;
- vlapic->timer_divide_count = 0x1 << (tmp2 & 0x7);
-
- vlapic_set_reg(vlapic, APIC_TDCR, val);
-
- HVM_DBG_LOG(DBG_LEVEL_VLAPIC_TIMER, "timer divide count is 0x%x",
- vlapic->timer_divide_count);
- }
+ vlapic_set_tdcr(vlapic, val & 0xb);
+ HVM_DBG_LOG(DBG_LEVEL_VLAPIC_TIMER, "timer divisor is 0x%x",
+ vlapic->timer_divisor);
break;
default:
- printk("Local APIC Write to read-only register\n");
+ gdprintk(XENLOG_WARNING,
+ "Local APIC Write to read-only register 0x%x\n", offset);
break;
}
}
static int vlapic_range(struct vcpu *v, unsigned long addr)
{
- struct vlapic *vlapic = VLAPIC(v);
+ struct vlapic *vlapic = vcpu_vlapic(v);
- if ( vlapic_global_enabled(vlapic) &&
- (addr >= vlapic->base_address) &&
- (addr < vlapic->base_address + VLOCAL_APIC_MEM_LENGTH) )
- return 1;
-
- return 0;
+ return (vlapic_global_enabled(vlapic) &&
+ (addr >= vlapic->base_address) &&
+ (addr < vlapic->base_address + PAGE_SIZE));
}
struct hvm_mmio_handler vlapic_mmio_handler = {
@@ -775,18 +790,9 @@ struct hvm_mmio_handler vlapic_mmio_handler = {
void vlapic_msr_set(struct vlapic *vlapic, uint64_t value)
{
- /* When apic disabled */
- if ( vlapic == NULL )
- return;
-
- if ( vlapic->vcpu_id )
- value &= ~MSR_IA32_APICBASE_BSP;
-
vlapic->apic_base_msr = value;
- vlapic->base_address = vlapic->apic_base_msr &
- MSR_IA32_APICBASE_BASE;
+ vlapic->base_address = vlapic->apic_base_msr & MSR_IA32_APICBASE_BASE;
- /* with FSB delivery interrupt, we can restart APIC functionality */
if ( !(value & MSR_IA32_APICBASE_ENABLE) )
set_bit(_VLAPIC_GLOB_DISABLE, &vlapic->status );
else
@@ -800,7 +806,6 @@ void vlapic_msr_set(struct vlapic *vlapic, uint64_t value)
void vlapic_timer_fn(void *data)
{
struct vlapic *vlapic = data;
- struct vcpu *v;
uint32_t timer_vector;
s_time_t now;
@@ -808,14 +813,13 @@ void vlapic_timer_fn(void *data)
!vlapic_lvt_enabled(vlapic, APIC_LVTT)) )
return;
- v = vlapic->vcpu;
timer_vector = vlapic_lvt_vector(vlapic, APIC_LVTT);
now = NOW();
vlapic->timer_last_update = now;
- if ( vlapic_test_and_set_vector(timer_vector, vlapic->regs + APIC_IRR) )
- vlapic->intr_pending_count[timer_vector]++;
+ if ( vlapic_test_and_set_irr(timer_vector, vlapic) )
+ vlapic->timer_pending_count++;
if ( vlapic_lvtt_period(vlapic) )
{
@@ -824,20 +828,14 @@ void vlapic_timer_fn(void *data)
vlapic_set_reg(vlapic, APIC_TMCCT, tmict);
- offset = APIC_BUS_CYCLE_NS *
- vlapic->timer_divide_count * tmict;
+ offset = APIC_BUS_CYCLE_NS * vlapic->timer_divisor * tmict;
set_timer(&vlapic->vlapic_timer, now + offset);
}
else
vlapic_set_reg(vlapic, APIC_TMCCT, 0);
-#if 0
- if ( test_bit(_VCPUF_running, &v->vcpu_flags) )
- {
- /* TODO: add guest time handling here */
- }
-#endif
+ vcpu_kick(vlapic_vcpu(vlapic));
HVM_DBG_LOG(DBG_LEVEL_VLAPIC_TIMER,
"now 0x%016"PRIx64", expire @ 0x%016"PRIx64", "
@@ -847,147 +845,89 @@ void vlapic_timer_fn(void *data)
vlapic_get_reg(vlapic, APIC_TMCCT));
}
-#if 0
-static int
-vlapic_check_direct_intr(struct vcpu *v, int * mode)
-{
- struct vlapic *vlapic = VLAPIC(v);
- int type;
-
- type = fls(vlapic->direct_intr.deliver_mode) - 1;
- if ( type == -1 )
- return -1;
-
- *mode = type;
- return 0;
-}
-#endif
-
int vlapic_accept_pic_intr(struct vcpu *v)
{
- struct vlapic *vlapic = VLAPIC(v);
+ struct vlapic *vlapic = vcpu_vlapic(v);
return vlapic ? test_bit(_VLAPIC_BSP_ACCEPT_PIC, &vlapic->status) : 1;
}
int cpu_get_apic_interrupt(struct vcpu *v, int *mode)
{
- struct vlapic *vlapic = VLAPIC(v);
-
- if ( vlapic && vlapic_enabled(vlapic) )
- {
- int highest_irr = vlapic_find_highest_irr(vlapic);
-
- if ( highest_irr != -1 &&
- ( (highest_irr & 0xF0) > vlapic_get_reg(vlapic, APIC_PROCPRI) ) )
- {
- if ( highest_irr < 0x10 )
- {
- uint32_t err_vector;
-
- vlapic->err_status |= 0x20;
- err_vector = vlapic_lvt_vector(vlapic, APIC_LVTERR);
-
- HVM_DBG_LOG(DBG_LEVEL_VLAPIC,
- "Sending an illegal vector 0x%x.", highest_irr);
-
- vlapic_set_vector(err_vector, vlapic->regs + APIC_IRR);
- highest_irr = err_vector;
- }
-
- *mode = APIC_DM_FIXED;
- return highest_irr;
- }
- }
- return -1;
-}
+ struct vlapic *vlapic = vcpu_vlapic(v);
+ int highest_irr;
-int cpu_has_apic_interrupt(struct vcpu* v)
-{
- struct vlapic *vlapic = VLAPIC(v);
+ if ( !vlapic || !vlapic_enabled(vlapic) )
+ return -1;
- if (vlapic && vlapic_enabled(vlapic)) {
- int highest_irr = vlapic_find_highest_irr(vlapic);
+ highest_irr = vlapic_find_highest_irr(vlapic);
+ if ( (highest_irr == -1) ||
+ ((highest_irr & 0xF0) <= vlapic_get_ppr(vlapic)) )
+ return -1;
- if ( highest_irr != -1 &&
- ( (highest_irr & 0xF0) > vlapic_get_reg(vlapic, APIC_PROCPRI) ) ) {
- return 1;
- }
- }
- return 0;
+ *mode = APIC_DM_FIXED;
+ return highest_irr;
}
/* check to see if there is pending interrupt */
int cpu_has_pending_irq(struct vcpu *v)
{
struct hvm_domain *plat = &v->domain->arch.hvm_domain;
+ int dummy;
/* APIC */
- if ( cpu_has_apic_interrupt(v) ) return 1;
-
+ if ( cpu_get_apic_interrupt(v, &dummy) != -1 )
+ return 1;
+
/* PIC */
- if ( !vlapic_accept_pic_intr(v) ) return 0;
+ if ( !vlapic_accept_pic_intr(v) )
+ return 0;
return plat->interrupt_request;
}
void vlapic_post_injection(struct vcpu *v, int vector, int deliver_mode)
{
- struct vlapic *vlapic = VLAPIC(v);
+ struct vlapic *vlapic = vcpu_vlapic(v);
if ( unlikely(vlapic == NULL) )
return;
- switch ( deliver_mode ) {
+ switch ( deliver_mode )
+ {
case APIC_DM_FIXED:
case APIC_DM_LOWEST:
vlapic_set_vector(vector, vlapic->regs + APIC_ISR);
- vlapic_clear_vector(vector, vlapic->regs + APIC_IRR);
- vlapic_update_ppr(vlapic);
-
- if ( vector == vlapic_lvt_vector(vlapic, APIC_LVTT) )
+ vlapic_clear_irr(vector, vlapic);
+ if ( (vector == vlapic_lvt_vector(vlapic, APIC_LVTT)) &&
+ (vlapic->timer_pending_count != 0) )
{
- if ( vlapic->intr_pending_count[vector] > 0 )
- {
- vlapic->intr_pending_count[vector]--;
- vlapic_test_and_set_vector(vector, vlapic->regs + APIC_IRR);
- }
+ vlapic->timer_pending_count--;
+ vlapic_set_irr(vector, vlapic);
}
break;
- /*XXX deal with these later */
case APIC_DM_REMRD:
- printk("Ignore deliver mode 3 in vlapic_post_injection\n");
+ gdprintk(XENLOG_WARNING, "Ignoring delivery mode 3.\n");
break;
case APIC_DM_SMI:
case APIC_DM_NMI:
case APIC_DM_INIT:
case APIC_DM_STARTUP:
- vlapic->direct_intr.deliver_mode &= (1 << (deliver_mode >> 8));
break;
default:
- printk("<vlapic_post_injection> invalid deliver mode\n");
+ gdprintk(XENLOG_WARNING, "Invalid delivery mode\n");
break;
}
}
static int vlapic_reset(struct vlapic *vlapic)
{
- struct vcpu *v;
+ struct vcpu *v = vlapic_vcpu(vlapic);
int i;
- ASSERT( vlapic != NULL );
-
- v = vlapic->vcpu;
-
- ASSERT( v != NULL );
-
- vlapic->domain = v->domain;
-
- vlapic->vcpu_id = v->vcpu_id;
-
vlapic_set_reg(vlapic, APIC_ID, v->vcpu_id << 24);
vlapic_set_reg(vlapic, APIC_LVR, VLAPIC_VERSION);
@@ -1001,30 +941,13 @@ static int vlapic_reset(struct vlapic *vlapic)
vlapic->apic_base_msr = MSR_IA32_APICBASE_ENABLE | APIC_DEFAULT_PHYS_BASE;
- if ( v->vcpu_id == 0 )
- vlapic->apic_base_msr |= MSR_IA32_APICBASE_BSP;
+ vlapic->flush_tpr_threshold = 0;
+
+ vlapic_set_tdcr(vlapic, 0);
vlapic->base_address = vlapic->apic_base_msr &
MSR_IA32_APICBASE_BASE;
- hvm_vioapic_add_lapic(vlapic, v);
-
- init_timer(&vlapic->vlapic_timer,
- vlapic_timer_fn, vlapic, v->processor);
-
-#ifdef VLAPIC_NO_BIOS
- /*
- * XXX According to mp sepcific, BIOS will enable LVT0/1,
- * remove it after BIOS enabled
- */
- if ( !v->vcpu_id )
- {
- vlapic_set_reg(vlapic, APIC_LVT0, APIC_MODE_EXTINT << 8);
- vlapic_set_reg(vlapic, APIC_LVT1, APIC_MODE_NMI << 8);
- set_bit(_VLAPIC_BSP_ACCEPT_PIC, &vlapic->status);
- }
-#endif
-
HVM_DBG_LOG(DBG_LEVEL_VLAPIC,
"vcpu=%p, id=%d, vlapic_apic_base_msr=0x%016"PRIx64", "
"base_address=0x%0lx.",
@@ -1036,38 +959,48 @@ static int vlapic_reset(struct vlapic *vlapic)
int vlapic_init(struct vcpu *v)
{
- struct vlapic *vlapic = NULL;
-
- ASSERT( v != NULL );
+ struct vlapic *vlapic = vcpu_vlapic(v);
HVM_DBG_LOG(DBG_LEVEL_VLAPIC, "vlapic_init %d", v->vcpu_id);
- vlapic = xmalloc_bytes(sizeof(struct vlapic));
- if ( vlapic == NULL )
- {
- printk("malloc vlapic error for vcpu %x\n", v->vcpu_id);
- return -ENOMEM;
- }
-
- memset(vlapic, 0, sizeof(struct vlapic));
-
vlapic->regs_page = alloc_domheap_page(NULL);
if ( vlapic->regs_page == NULL )
{
- printk("malloc vlapic regs error for vcpu %x\n", v->vcpu_id);
+ dprintk(XENLOG_ERR, "malloc vlapic regs error for vcpu %x\n",
+ v->vcpu_id);
xfree(vlapic);
return -ENOMEM;
}
vlapic->regs = map_domain_page_global(page_to_mfn(vlapic->regs_page));
-
memset(vlapic->regs, 0, PAGE_SIZE);
- VLAPIC(v) = vlapic;
+ vlapic_reset(vlapic);
- vlapic->vcpu = v;
+ if ( v->vcpu_id == 0 )
+ vlapic->apic_base_msr |= MSR_IA32_APICBASE_BSP;
- vlapic_reset(vlapic);
+ init_timer(&vlapic->vlapic_timer,
+ vlapic_timer_fn, vlapic, v->processor);
+
+#ifdef VLAPIC_NO_BIOS
+ /* According to mp specification, BIOS will enable LVT0/1. */
+ if ( v->vcpu_id == 0 )
+ {
+ vlapic_set_reg(vlapic, APIC_LVT0, APIC_MODE_EXTINT << 8);
+ vlapic_set_reg(vlapic, APIC_LVT1, APIC_MODE_NMI << 8);
+ set_bit(_VLAPIC_BSP_ACCEPT_PIC, &vlapic->status);
+ }
+#endif
return 0;
}
+
+void vlapic_destroy(struct vcpu *v)
+{
+ struct vlapic *vlapic = vcpu_vlapic(v);
+
+ kill_timer(&vlapic->vlapic_timer);
+ unmap_domain_page_global(vlapic->regs);
+ free_domheap_page(vlapic->regs_page);
+}
diff --git a/xen/arch/x86/hvm/vmx/io.c b/xen/arch/x86/hvm/vmx/io.c
index 07731ad251..5c904710d2 100644
--- a/xen/arch/x86/hvm/vmx/io.c
+++ b/xen/arch/x86/hvm/vmx/io.c
@@ -63,11 +63,30 @@ disable_irq_window(struct vcpu *v)
static inline int is_interruptibility_state(void)
{
- int interruptibility;
- __vmread(GUEST_INTERRUPTIBILITY_INFO, &interruptibility);
- return interruptibility;
+ return __vmread(GUEST_INTERRUPTIBILITY_INFO);
}
+#ifdef __x86_64__
+static void update_tpr_threshold(struct vlapic *vlapic)
+{
+ int highest_irr, tpr;
+
+ /* Clear the work-to-do flag /then/ do the work. */
+ vlapic->flush_tpr_threshold = 0;
+ mb();
+
+ highest_irr = vlapic_find_highest_irr(vlapic);
+ tpr = vlapic_get_reg(vlapic, APIC_TASKPRI) & 0xF0;
+
+ if ( highest_irr == -1 )
+ __vmwrite(TPR_THRESHOLD, 0);
+ else
+ __vmwrite(TPR_THRESHOLD,
+ (highest_irr > tpr) ? (tpr >> 4) : (highest_irr >> 4));
+}
+#else
+#define update_tpr_threshold(v) ((void)0)
+#endif
asmlinkage void vmx_intr_assist(void)
{
@@ -75,16 +94,14 @@ asmlinkage void vmx_intr_assist(void)
int highest_vector;
unsigned long eflags;
struct vcpu *v = current;
+ struct vlapic *vlapic = vcpu_vlapic(v);
struct hvm_domain *plat=&v->domain->arch.hvm_domain;
struct periodic_time *pt = &plat->pl_time.periodic_tm;
- struct hvm_virpic *pic= &plat->vpic;
+ struct vpic *pic= &plat->vpic;
unsigned int idtv_info_field;
unsigned long inst_len;
int has_ext_irq;
- if ( v->vcpu_id == 0 )
- hvm_pic_assist(v);
-
if ( (v->vcpu_id == 0) && pt->enabled && pt->pending_intr_nr ) {
pic_set_irq(pic, pt->irq, 0);
pic_set_irq(pic, pt->irq, 1);
@@ -98,6 +115,9 @@ asmlinkage void vmx_intr_assist(void)
pic_set_xen_irq(pic, callback_irq, local_events_need_delivery());
}
+ if ( vlapic_enabled(vlapic) && vlapic->flush_tpr_threshold )
+ update_tpr_threshold(vlapic);
+
has_ext_irq = cpu_has_pending_irq(v);
if (unlikely(v->arch.hvm_vmx.vector_injected)) {
@@ -107,7 +127,7 @@ asmlinkage void vmx_intr_assist(void)
}
/* This could be moved earlier in the VMX resume sequence. */
- __vmread(IDT_VECTORING_INFO_FIELD, &idtv_info_field);
+ idtv_info_field = __vmread(IDT_VECTORING_INFO_FIELD);
if (unlikely(idtv_info_field & INTR_INFO_VALID_MASK)) {
__vmwrite(VM_ENTRY_INTR_INFO_FIELD, idtv_info_field);
@@ -116,14 +136,12 @@ asmlinkage void vmx_intr_assist(void)
* and interrupts. If we get here then delivery of some event caused a
* fault, and this always results in defined VM_EXIT_INSTRUCTION_LEN.
*/
- __vmread(VM_EXIT_INSTRUCTION_LEN, &inst_len); /* Safe */
+ inst_len = __vmread(VM_EXIT_INSTRUCTION_LEN); /* Safe */
__vmwrite(VM_ENTRY_INSTRUCTION_LEN, inst_len);
- if (unlikely(idtv_info_field & 0x800)) { /* valid error code */
- unsigned long error_code;
- __vmread(IDT_VECTORING_ERROR_CODE, &error_code);
- __vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE, error_code);
- }
+ if (unlikely(idtv_info_field & 0x800)) /* valid error code */
+ __vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE,
+ __vmread(IDT_VECTORING_ERROR_CODE));
if (unlikely(has_ext_irq))
enable_irq_window(v);
@@ -141,7 +159,7 @@ asmlinkage void vmx_intr_assist(void)
return;
}
- __vmread(GUEST_RFLAGS, &eflags);
+ eflags = __vmread(GUEST_RFLAGS);
if (irq_masked(eflags)) {
enable_irq_window(v);
return;
diff --git a/xen/arch/x86/hvm/vmx/vmcs.c b/xen/arch/x86/hvm/vmx/vmcs.c
index 3892ffa1c3..91328ecc6d 100644
--- a/xen/arch/x86/hvm/vmx/vmcs.c
+++ b/xen/arch/x86/hvm/vmx/vmcs.c
@@ -56,7 +56,7 @@
CPU_BASED_INVDPG_EXITING | \
CPU_BASED_MWAIT_EXITING | \
CPU_BASED_MOV_DR_EXITING | \
- CPU_BASED_ACTIVATE_IO_BITMAP | \
+ CPU_BASED_UNCOND_IO_EXITING | \
CPU_BASED_USE_TSC_OFFSETING )
/* Basic flags for VM-Exit controls. */
@@ -145,7 +145,7 @@ static struct vmcs_struct *vmx_alloc_vmcs(void)
if ( (vmcs = alloc_xenheap_page()) == NULL )
{
- DPRINTK("Failed to allocate VMCS.\n");
+ gdprintk(XENLOG_WARNING, "Failed to allocate VMCS.\n");
return NULL;
}
@@ -193,13 +193,7 @@ void vmx_vmcs_enter(struct vcpu *v)
{
/*
* NB. We must *always* run an HVM VCPU on its own VMCS, except for
- * vmx_vmcs_enter/exit critical regions. This leads to some XXX TODOs XXX:
- * 1. Move construct_vmcs() much earlier, to domain creation or
- * context initialisation.
- * 2. VMPTRLD as soon as we context-switch to a HVM VCPU.
- * 3. VMCS destruction needs to happen later (from domain_destroy()).
- * We can relax this a bit if a paused VCPU always commits its
- * architectural state to a software structure.
+ * vmx_vmcs_enter/exit critical regions.
*/
if ( v == current )
return;
@@ -218,7 +212,7 @@ void vmx_vmcs_exit(struct vcpu *v)
/* Don't confuse arch_vmx_do_resume (for @v or @current!) */
vmx_clear_vmcs(v);
- if ( hvm_guest(current) )
+ if ( is_hvm_vcpu(current) )
vmx_load_vmcs(current);
spin_unlock(&v->arch.hvm_vmx.vmcs_lock);
@@ -235,34 +229,7 @@ void vmx_free_host_vmcs(struct vmcs_struct *vmcs)
vmx_free_vmcs(vmcs);
}
-static inline int construct_vmcs_controls(struct arch_vmx_struct *arch_vmx)
-{
- int error = 0;
-
- error |= __vmwrite(PIN_BASED_VM_EXEC_CONTROL, vmx_pin_based_exec_control);
-
- error |= __vmwrite(VM_EXIT_CONTROLS, vmx_vmexit_control);
-
- error |= __vmwrite(VM_ENTRY_CONTROLS, vmx_vmentry_control);
-
- error |= __vmwrite(IO_BITMAP_A, virt_to_maddr(arch_vmx->io_bitmap_a));
- error |= __vmwrite(IO_BITMAP_B, virt_to_maddr(arch_vmx->io_bitmap_b));
-
-#ifdef CONFIG_X86_PAE
- /* On PAE bitmaps may in future be above 4GB. Write high words. */
- error |= __vmwrite(IO_BITMAP_A_HIGH,
- (paddr_t)virt_to_maddr(arch_vmx->io_bitmap_a) >> 32);
- error |= __vmwrite(IO_BITMAP_B_HIGH,
- (paddr_t)virt_to_maddr(arch_vmx->io_bitmap_b) >> 32);
-#endif
-
- return error;
-}
-
-#define GUEST_LAUNCH_DS 0x08
-#define GUEST_LAUNCH_CS 0x10
#define GUEST_SEGMENT_LIMIT 0xffffffff
-#define HOST_SEGMENT_LIMIT 0xffffffff
struct host_execution_env {
/* selectors */
@@ -290,7 +257,7 @@ struct host_execution_env {
static void vmx_set_host_env(struct vcpu *v)
{
- unsigned int tr, cpu, error = 0;
+ unsigned int tr, cpu;
struct host_execution_env host_env;
struct Xgt_desc_struct desc;
@@ -298,127 +265,97 @@ static void vmx_set_host_env(struct vcpu *v)
__asm__ __volatile__ ("sidt (%0) \n" :: "a"(&desc) : "memory");
host_env.idtr_limit = desc.size;
host_env.idtr_base = desc.address;
- error |= __vmwrite(HOST_IDTR_BASE, host_env.idtr_base);
+ __vmwrite(HOST_IDTR_BASE, host_env.idtr_base);
__asm__ __volatile__ ("sgdt (%0) \n" :: "a"(&desc) : "memory");
host_env.gdtr_limit = desc.size;
host_env.gdtr_base = desc.address;
- error |= __vmwrite(HOST_GDTR_BASE, host_env.gdtr_base);
+ __vmwrite(HOST_GDTR_BASE, host_env.gdtr_base);
__asm__ __volatile__ ("str (%0) \n" :: "a"(&tr) : "memory");
host_env.tr_selector = tr;
host_env.tr_limit = sizeof(struct tss_struct);
host_env.tr_base = (unsigned long) &init_tss[cpu];
- error |= __vmwrite(HOST_TR_SELECTOR, host_env.tr_selector);
- error |= __vmwrite(HOST_TR_BASE, host_env.tr_base);
- error |= __vmwrite(HOST_RSP, (unsigned long)get_stack_bottom());
+ __vmwrite(HOST_TR_SELECTOR, host_env.tr_selector);
+ __vmwrite(HOST_TR_BASE, host_env.tr_base);
+ __vmwrite(HOST_RSP, (unsigned long)get_stack_bottom());
}
-static void vmx_do_launch(struct vcpu *v)
+static void construct_vmcs(struct vcpu *v)
{
-/* Update CR3, CR0, CR4, GDT, LDT, TR */
- unsigned int error = 0;
unsigned long cr0, cr4;
+ union vmcs_arbytes arbytes;
- if (v->vcpu_id == 0)
- hvm_setup_platform(v->domain);
-
- __asm__ __volatile__ ("mov %%cr0,%0" : "=r" (cr0) : );
+ vmx_vmcs_enter(v);
- error |= __vmwrite(GUEST_CR0, cr0);
- cr0 &= ~X86_CR0_PG;
- error |= __vmwrite(CR0_READ_SHADOW, cr0);
- error |= __vmwrite(CPU_BASED_VM_EXEC_CONTROL, vmx_cpu_based_exec_control);
+ /* VMCS controls. */
+ __vmwrite(PIN_BASED_VM_EXEC_CONTROL, vmx_pin_based_exec_control);
+ __vmwrite(VM_EXIT_CONTROLS, vmx_vmexit_control);
+ __vmwrite(VM_ENTRY_CONTROLS, vmx_vmentry_control);
+ __vmwrite(CPU_BASED_VM_EXEC_CONTROL, vmx_cpu_based_exec_control);
v->arch.hvm_vcpu.u.vmx.exec_control = vmx_cpu_based_exec_control;
- __asm__ __volatile__ ("mov %%cr4,%0" : "=r" (cr4) : );
+ /* Host data selectors. */
+ __vmwrite(HOST_SS_SELECTOR, __HYPERVISOR_DS);
+ __vmwrite(HOST_DS_SELECTOR, __HYPERVISOR_DS);
+ __vmwrite(HOST_ES_SELECTOR, __HYPERVISOR_DS);
+#if defined(__i386__)
+ __vmwrite(HOST_FS_SELECTOR, __HYPERVISOR_DS);
+ __vmwrite(HOST_GS_SELECTOR, __HYPERVISOR_DS);
+ __vmwrite(HOST_FS_BASE, 0);
+ __vmwrite(HOST_GS_BASE, 0);
+#elif defined(__x86_64__)
+ {
+ unsigned long msr;
+ rdmsrl(MSR_FS_BASE, msr); __vmwrite(HOST_FS_BASE, msr);
+ rdmsrl(MSR_GS_BASE, msr); __vmwrite(HOST_GS_BASE, msr);
+ }
+#endif
- error |= __vmwrite(GUEST_CR4, cr4 & ~X86_CR4_PSE);
- cr4 &= ~(X86_CR4_PGE | X86_CR4_VMXE | X86_CR4_PAE);
+ /* Host control registers. */
+ __vmwrite(HOST_CR0, read_cr0());
+ __vmwrite(HOST_CR4, read_cr4());
- error |= __vmwrite(CR4_READ_SHADOW, cr4);
+ /* Host CS:RIP. */
+ __vmwrite(HOST_CS_SELECTOR, __HYPERVISOR_CS);
+ __vmwrite(HOST_RIP, (unsigned long)vmx_asm_vmexit_handler);
- hvm_stts(v);
+ /* MSR intercepts. */
+ __vmwrite(VM_EXIT_MSR_LOAD_ADDR, 0);
+ __vmwrite(VM_EXIT_MSR_STORE_ADDR, 0);
+ __vmwrite(VM_EXIT_MSR_STORE_COUNT, 0);
+ __vmwrite(VM_EXIT_MSR_LOAD_COUNT, 0);
+ __vmwrite(VM_ENTRY_MSR_LOAD_COUNT, 0);
- if(hvm_apic_support(v->domain))
- vlapic_init(v);
+ __vmwrite(VM_ENTRY_INTR_INFO_FIELD, 0);
- vmx_set_host_env(v);
- init_timer(&v->arch.hvm_vcpu.hlt_timer, hlt_timer_fn, v, v->processor);
+ __vmwrite(CR0_GUEST_HOST_MASK, ~0UL);
+ __vmwrite(CR4_GUEST_HOST_MASK, ~0UL);
- error |= __vmwrite(GUEST_LDTR_SELECTOR, 0);
- error |= __vmwrite(GUEST_LDTR_BASE, 0);
- error |= __vmwrite(GUEST_LDTR_LIMIT, 0);
+ __vmwrite(PAGE_FAULT_ERROR_CODE_MASK, 0);
+ __vmwrite(PAGE_FAULT_ERROR_CODE_MATCH, 0);
- error |= __vmwrite(GUEST_TR_BASE, 0);
- error |= __vmwrite(GUEST_TR_LIMIT, 0xff);
+ __vmwrite(CR3_TARGET_COUNT, 0);
- shadow_update_paging_modes(v);
- printk("%s(): GUEST_CR3<=%08lx, HOST_CR3<=%08lx\n",
- __func__, v->arch.hvm_vcpu.hw_cr3, v->arch.cr3);
- __vmwrite(GUEST_CR3, v->arch.hvm_vcpu.hw_cr3);
- __vmwrite(HOST_CR3, v->arch.cr3);
+ __vmwrite(GUEST_ACTIVITY_STATE, 0);
- v->arch.schedule_tail = arch_vmx_do_resume;
+ /* Guest segment bases. */
+ __vmwrite(GUEST_ES_BASE, 0);
+ __vmwrite(GUEST_SS_BASE, 0);
+ __vmwrite(GUEST_DS_BASE, 0);
+ __vmwrite(GUEST_FS_BASE, 0);
+ __vmwrite(GUEST_GS_BASE, 0);
+ __vmwrite(GUEST_CS_BASE, 0);
- /* init guest tsc to start from 0 */
- hvm_set_guest_time(v, 0);
-}
+ /* Guest segment limits. */
+ __vmwrite(GUEST_ES_LIMIT, GUEST_SEGMENT_LIMIT);
+ __vmwrite(GUEST_SS_LIMIT, GUEST_SEGMENT_LIMIT);
+ __vmwrite(GUEST_DS_LIMIT, GUEST_SEGMENT_LIMIT);
+ __vmwrite(GUEST_FS_LIMIT, GUEST_SEGMENT_LIMIT);
+ __vmwrite(GUEST_GS_LIMIT, GUEST_SEGMENT_LIMIT);
+ __vmwrite(GUEST_CS_LIMIT, GUEST_SEGMENT_LIMIT);
-/*
- * Initially set the same environement as host.
- */
-static inline int construct_init_vmcs_guest(cpu_user_regs_t *regs)
-{
- int error = 0;
- union vmcs_arbytes arbytes;
- unsigned long dr7;
- unsigned long eflags;
-
- /* MSR */
- error |= __vmwrite(VM_EXIT_MSR_LOAD_ADDR, 0);
- error |= __vmwrite(VM_EXIT_MSR_STORE_ADDR, 0);
- error |= __vmwrite(VM_EXIT_MSR_STORE_COUNT, 0);
- error |= __vmwrite(VM_EXIT_MSR_LOAD_COUNT, 0);
- error |= __vmwrite(VM_ENTRY_MSR_LOAD_COUNT, 0);
-
- error |= __vmwrite(VM_ENTRY_INTR_INFO_FIELD, 0);
-
- error |= __vmwrite(CR0_GUEST_HOST_MASK, ~0UL);
- error |= __vmwrite(CR4_GUEST_HOST_MASK, ~0UL);
-
- error |= __vmwrite(PAGE_FAULT_ERROR_CODE_MASK, 0);
- error |= __vmwrite(PAGE_FAULT_ERROR_CODE_MATCH, 0);
-
- error |= __vmwrite(CR3_TARGET_COUNT, 0);
-
- error |= __vmwrite(GUEST_ACTIVITY_STATE, 0);
-
- /* Guest Selectors */
- error |= __vmwrite(GUEST_ES_SELECTOR, GUEST_LAUNCH_DS);
- error |= __vmwrite(GUEST_SS_SELECTOR, GUEST_LAUNCH_DS);
- error |= __vmwrite(GUEST_DS_SELECTOR, GUEST_LAUNCH_DS);
- error |= __vmwrite(GUEST_FS_SELECTOR, GUEST_LAUNCH_DS);
- error |= __vmwrite(GUEST_GS_SELECTOR, GUEST_LAUNCH_DS);
- error |= __vmwrite(GUEST_CS_SELECTOR, GUEST_LAUNCH_CS);
-
- /* Guest segment bases */
- error |= __vmwrite(GUEST_ES_BASE, 0);
- error |= __vmwrite(GUEST_SS_BASE, 0);
- error |= __vmwrite(GUEST_DS_BASE, 0);
- error |= __vmwrite(GUEST_FS_BASE, 0);
- error |= __vmwrite(GUEST_GS_BASE, 0);
- error |= __vmwrite(GUEST_CS_BASE, 0);
-
- /* Guest segment Limits */
- error |= __vmwrite(GUEST_ES_LIMIT, GUEST_SEGMENT_LIMIT);
- error |= __vmwrite(GUEST_SS_LIMIT, GUEST_SEGMENT_LIMIT);
- error |= __vmwrite(GUEST_DS_LIMIT, GUEST_SEGMENT_LIMIT);
- error |= __vmwrite(GUEST_FS_LIMIT, GUEST_SEGMENT_LIMIT);
- error |= __vmwrite(GUEST_GS_LIMIT, GUEST_SEGMENT_LIMIT);
- error |= __vmwrite(GUEST_CS_LIMIT, GUEST_SEGMENT_LIMIT);
-
- /* Guest segment AR bytes */
+ /* Guest segment AR bytes. */
arbytes.bytes = 0;
arbytes.fields.seg_type = 0x3; /* type = 3 */
arbytes.fields.s = 1; /* code or data, i.e. not system */
@@ -427,146 +364,88 @@ static inline int construct_init_vmcs_guest(cpu_user_regs_t *regs)
arbytes.fields.default_ops_size = 1; /* 32-bit */
arbytes.fields.g = 1;
arbytes.fields.null_bit = 0; /* not null */
-
- error |= __vmwrite(GUEST_ES_AR_BYTES, arbytes.bytes);
- error |= __vmwrite(GUEST_SS_AR_BYTES, arbytes.bytes);
- error |= __vmwrite(GUEST_DS_AR_BYTES, arbytes.bytes);
- error |= __vmwrite(GUEST_FS_AR_BYTES, arbytes.bytes);
- error |= __vmwrite(GUEST_GS_AR_BYTES, arbytes.bytes);
-
+ __vmwrite(GUEST_ES_AR_BYTES, arbytes.bytes);
+ __vmwrite(GUEST_SS_AR_BYTES, arbytes.bytes);
+ __vmwrite(GUEST_DS_AR_BYTES, arbytes.bytes);
+ __vmwrite(GUEST_FS_AR_BYTES, arbytes.bytes);
+ __vmwrite(GUEST_GS_AR_BYTES, arbytes.bytes);
arbytes.fields.seg_type = 0xb; /* type = 0xb */
- error |= __vmwrite(GUEST_CS_AR_BYTES, arbytes.bytes);
+ __vmwrite(GUEST_CS_AR_BYTES, arbytes.bytes);
- /* Guest GDT */
- error |= __vmwrite(GUEST_GDTR_BASE, 0);
- error |= __vmwrite(GUEST_GDTR_LIMIT, 0);
+ /* Guest GDT. */
+ __vmwrite(GUEST_GDTR_BASE, 0);
+ __vmwrite(GUEST_GDTR_LIMIT, 0);
- /* Guest IDT */
- error |= __vmwrite(GUEST_IDTR_BASE, 0);
- error |= __vmwrite(GUEST_IDTR_LIMIT, 0);
+ /* Guest IDT. */
+ __vmwrite(GUEST_IDTR_BASE, 0);
+ __vmwrite(GUEST_IDTR_LIMIT, 0);
- /* Guest LDT & TSS */
+ /* Guest LDT and TSS. */
arbytes.fields.s = 0; /* not code or data segement */
arbytes.fields.seg_type = 0x2; /* LTD */
arbytes.fields.default_ops_size = 0; /* 16-bit */
arbytes.fields.g = 0;
- error |= __vmwrite(GUEST_LDTR_AR_BYTES, arbytes.bytes);
-
+ __vmwrite(GUEST_LDTR_AR_BYTES, arbytes.bytes);
arbytes.fields.seg_type = 0xb; /* 32-bit TSS (busy) */
- error |= __vmwrite(GUEST_TR_AR_BYTES, arbytes.bytes);
- /* CR3 is set in vmx_final_setup_guest */
-
- error |= __vmwrite(GUEST_RSP, 0);
- error |= __vmwrite(GUEST_RIP, regs->eip);
-
- /* Guest EFLAGS */
- eflags = regs->eflags & ~HVM_EFLAGS_RESERVED_0; /* clear 0s */
- eflags |= HVM_EFLAGS_RESERVED_1; /* set 1s */
- error |= __vmwrite(GUEST_RFLAGS, eflags);
+ __vmwrite(GUEST_TR_AR_BYTES, arbytes.bytes);
- error |= __vmwrite(GUEST_INTERRUPTIBILITY_INFO, 0);
- __asm__ __volatile__ ("mov %%dr7, %0\n" : "=r" (dr7));
- error |= __vmwrite(GUEST_DR7, dr7);
- error |= __vmwrite(VMCS_LINK_POINTER, ~0UL);
+ __vmwrite(GUEST_INTERRUPTIBILITY_INFO, 0);
+ __vmwrite(GUEST_DR7, 0);
+ __vmwrite(VMCS_LINK_POINTER, ~0UL);
#if defined(__i386__)
- error |= __vmwrite(VMCS_LINK_POINTER_HIGH, ~0UL);
-#endif
-
- return error;
-}
-
-static inline int construct_vmcs_host(void)
-{
- int error = 0;
-#ifdef __x86_64__
- unsigned long fs_base;
- unsigned long gs_base;
+ __vmwrite(VMCS_LINK_POINTER_HIGH, ~0UL);
#endif
- unsigned long crn;
-
- /* Host Selectors */
- error |= __vmwrite(HOST_ES_SELECTOR, __HYPERVISOR_DS);
- error |= __vmwrite(HOST_SS_SELECTOR, __HYPERVISOR_DS);
- error |= __vmwrite(HOST_DS_SELECTOR, __HYPERVISOR_DS);
-#if defined(__i386__)
- error |= __vmwrite(HOST_FS_SELECTOR, __HYPERVISOR_DS);
- error |= __vmwrite(HOST_GS_SELECTOR, __HYPERVISOR_DS);
- error |= __vmwrite(HOST_FS_BASE, 0);
- error |= __vmwrite(HOST_GS_BASE, 0);
-
-#else
- rdmsrl(MSR_FS_BASE, fs_base);
- rdmsrl(MSR_GS_BASE, gs_base);
- error |= __vmwrite(HOST_FS_BASE, fs_base);
- error |= __vmwrite(HOST_GS_BASE, gs_base);
-#endif
- error |= __vmwrite(HOST_CS_SELECTOR, __HYPERVISOR_CS);
-
- __asm__ __volatile__ ("mov %%cr0,%0" : "=r" (crn) : );
- error |= __vmwrite(HOST_CR0, crn); /* same CR0 */
-
- /* CR3 is set in vmx_final_setup_hostos */
- __asm__ __volatile__ ("mov %%cr4,%0" : "=r" (crn) : );
- error |= __vmwrite(HOST_CR4, crn);
-
- error |= __vmwrite(HOST_RIP, (unsigned long) vmx_asm_vmexit_handler);
-#ifdef __x86_64__
- /* TBD: support cr8 for 64-bit guest */
- __vmwrite(VIRTUAL_APIC_PAGE_ADDR, 0);
+ __vmwrite(EXCEPTION_BITMAP, MONITOR_DEFAULT_EXCEPTION_BITMAP);
+
+ /* Guest CR0. */
+ cr0 = read_cr0();
+ v->arch.hvm_vmx.cpu_cr0 = cr0;
+ __vmwrite(GUEST_CR0, v->arch.hvm_vmx.cpu_cr0);
+ v->arch.hvm_vmx.cpu_shadow_cr0 = cr0 & ~(X86_CR0_PG | X86_CR0_TS);
+ __vmwrite(CR0_READ_SHADOW, v->arch.hvm_vmx.cpu_shadow_cr0);
+
+ /* Guest CR4. */
+ cr4 = read_cr4();
+ __vmwrite(GUEST_CR4, cr4 & ~X86_CR4_PSE);
+ v->arch.hvm_vmx.cpu_shadow_cr4 =
+ cr4 & ~(X86_CR4_PGE | X86_CR4_VMXE | X86_CR4_PAE);
+ __vmwrite(CR4_READ_SHADOW, v->arch.hvm_vmx.cpu_shadow_cr4);
+
+#ifdef __x86_64__
+ /* VLAPIC TPR optimisation. */
+ v->arch.hvm_vcpu.u.vmx.exec_control |= CPU_BASED_TPR_SHADOW;
+ v->arch.hvm_vcpu.u.vmx.exec_control &=
+ ~(CPU_BASED_CR8_STORE_EXITING | CPU_BASED_CR8_LOAD_EXITING);
+ __vmwrite(CPU_BASED_VM_EXEC_CONTROL, v->arch.hvm_vcpu.u.vmx.exec_control);
+ __vmwrite(VIRTUAL_APIC_PAGE_ADDR,
+ page_to_maddr(vcpu_vlapic(v)->regs_page));
__vmwrite(TPR_THRESHOLD, 0);
- __vmwrite(SECONDARY_VM_EXEC_CONTROL, 0);
#endif
- return error;
-}
-
-/*
- * the working VMCS pointer has been set properly
- * just before entering this function.
- */
-static int construct_vmcs(struct vcpu *v,
- cpu_user_regs_t *regs)
-{
- struct arch_vmx_struct *arch_vmx = &v->arch.hvm_vmx;
- int error;
-
- if ( (error = construct_vmcs_controls(arch_vmx)) ) {
- printk("construct_vmcs: construct_vmcs_controls failed.\n");
- return error;
- }
-
- /* host selectors */
- if ( (error = construct_vmcs_host()) ) {
- printk("construct_vmcs: construct_vmcs_host failed.\n");
- return error;
- }
-
- /* guest selectors */
- if ( (error = construct_init_vmcs_guest(regs)) ) {
- printk("construct_vmcs: construct_vmcs_guest failed.\n");
- return error;
- }
+ __vmwrite(GUEST_LDTR_SELECTOR, 0);
+ __vmwrite(GUEST_LDTR_BASE, 0);
+ __vmwrite(GUEST_LDTR_LIMIT, 0);
- if ( (error = __vmwrite(EXCEPTION_BITMAP,
- MONITOR_DEFAULT_EXCEPTION_BITMAP)) ) {
- printk("construct_vmcs: setting exception bitmap failed.\n");
- return error;
- }
+ __vmwrite(GUEST_TR_BASE, 0);
+ __vmwrite(GUEST_TR_LIMIT, 0xff);
- if ( regs->eflags & EF_TF )
- error = __vm_set_bit(EXCEPTION_BITMAP, EXCEPTION_BITMAP_DB);
- else
- error = __vm_clear_bit(EXCEPTION_BITMAP, EXCEPTION_BITMAP_DB);
+ shadow_update_paging_modes(v);
+ __vmwrite(GUEST_CR3, v->arch.hvm_vcpu.hw_cr3);
+ __vmwrite(HOST_CR3, v->arch.cr3);
- return error;
+ vmx_vmcs_exit(v);
}
int vmx_create_vmcs(struct vcpu *v)
{
if ( (v->arch.hvm_vmx.vmcs = vmx_alloc_vmcs()) == NULL )
return -ENOMEM;
+
__vmx_clear_vmcs(v);
+
+ construct_vmcs(v);
+
return 0;
}
@@ -579,28 +458,20 @@ void vmx_destroy_vmcs(struct vcpu *v)
vmx_clear_vmcs(v);
- free_xenheap_pages(arch_vmx->io_bitmap_a, IO_BITMAP_ORDER);
- free_xenheap_pages(arch_vmx->io_bitmap_b, IO_BITMAP_ORDER);
-
- arch_vmx->io_bitmap_a = NULL;
- arch_vmx->io_bitmap_b = NULL;
-
vmx_free_vmcs(arch_vmx->vmcs);
arch_vmx->vmcs = NULL;
}
void vm_launch_fail(unsigned long eflags)
{
- unsigned long error;
- __vmread(VM_INSTRUCTION_ERROR, &error);
+ unsigned long error = __vmread(VM_INSTRUCTION_ERROR);
printk("<vm_launch_fail> error code %lx\n", error);
__hvm_bug(guest_cpu_user_regs());
}
void vm_resume_fail(unsigned long eflags)
{
- unsigned long error;
- __vmread(VM_INSTRUCTION_ERROR, &error);
+ unsigned long error = __vmread(VM_INSTRUCTION_ERROR);
printk("<vm_resume_fail> error code %lx\n", error);
__hvm_bug(guest_cpu_user_regs());
}
@@ -623,34 +494,13 @@ void arch_vmx_do_resume(struct vcpu *v)
reset_stack_and_jump(vmx_asm_do_vmentry);
}
-void arch_vmx_do_launch(struct vcpu *v)
-{
- cpu_user_regs_t *regs = &current->arch.guest_context.user_regs;
-
- vmx_load_vmcs(v);
-
- if ( construct_vmcs(v, regs) < 0 )
- {
- if ( v->vcpu_id == 0 ) {
- printk("Failed to construct VMCS for BSP.\n");
- } else {
- printk("Failed to construct VMCS for AP %d.\n", v->vcpu_id);
- }
- domain_crash_synchronous();
- }
-
- vmx_do_launch(v);
- reset_stack_and_jump(vmx_asm_do_vmentry);
-}
-
-
/* Dump a section of VMCS */
static void print_section(char *header, uint32_t start,
uint32_t end, int incr)
{
uint32_t addr, j;
unsigned long val;
- int code;
+ int code, rc;
char *fmt[4] = {"0x%04lx ", "0x%016lx ", "0x%08lx ", "0x%016lx "};
char *err[4] = {"------ ", "------------------ ",
"---------- ", "------------------ "};
@@ -666,7 +516,8 @@ static void print_section(char *header, uint32_t start,
if (!(j&3))
printk("\n\t\t0x%08x: ", addr);
- if (!__vmread(addr, &val))
+ val = __vmread_safe(addr, &rc);
+ if (rc == 0)
printk(fmt[code], val);
else
printk("%s", err[code]);
@@ -699,20 +550,14 @@ static void vmcs_dump(unsigned char ch)
struct vcpu *v;
printk("*********** VMCS Areas **************\n");
- for_each_domain(d) {
+ for_each_domain ( d )
+ {
+ if ( !is_hvm_domain(d) )
+ continue;
printk("\n>>> Domain %d <<<\n", d->domain_id);
- for_each_vcpu(d, v) {
-
- /*
- * Presumably, if a domain is not an HVM guest,
- * the very first CPU will not pass this test
- */
- if (!hvm_guest(v)) {
- printk("\t\tNot HVM guest\n");
- break;
- }
+ for_each_vcpu ( d, v )
+ {
printk("\tVCPU %d\n", v->vcpu_id);
-
vmx_vmcs_enter(v);
vmcs_dump_vcpu();
vmx_vmcs_exit(v);
diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c
index ac1be73556..2eed6af1a3 100644
--- a/xen/arch/x86/hvm/vmx/vmx.c
+++ b/xen/arch/x86/hvm/vmx/vmx.c
@@ -47,114 +47,33 @@
#include <asm/hvm/vlapic.h>
#include <asm/x86_emulate.h>
-extern uint32_t vlapic_update_ppr(struct vlapic *vlapic);
-
-static DEFINE_PER_CPU(unsigned long, trace_values[5]);
-#define TRACE_VMEXIT(index,value) this_cpu(trace_values)[index]=value
-
static void vmx_ctxt_switch_from(struct vcpu *v);
static void vmx_ctxt_switch_to(struct vcpu *v);
-static int vmx_initialize_guest_resources(struct vcpu *v)
+static int vmx_vcpu_initialise(struct vcpu *v)
{
- struct domain *d = v->domain;
- struct vcpu *vc;
- void *io_bitmap_a, *io_bitmap_b;
int rc;
- v->arch.schedule_tail = arch_vmx_do_launch;
+ spin_lock_init(&v->arch.hvm_vmx.vmcs_lock);
+
+ v->arch.schedule_tail = arch_vmx_do_resume;
v->arch.ctxt_switch_from = vmx_ctxt_switch_from;
v->arch.ctxt_switch_to = vmx_ctxt_switch_to;
- if ( v->vcpu_id != 0 )
- return 1;
-
- if ( !shadow_mode_external(d) )
+ if ( (rc = vmx_create_vmcs(v)) != 0 )
{
- DPRINTK("Can't init HVM for dom %u vcpu %u: "
- "not in shadow external mode\n",
- d->domain_id, v->vcpu_id);
- domain_crash(d);
- }
-
- for_each_vcpu ( d, vc )
- {
- memset(&vc->arch.hvm_vmx, 0, sizeof(struct arch_vmx_struct));
-
- if ( (rc = vmx_create_vmcs(vc)) != 0 )
- {
- DPRINTK("Failed to create VMCS for vcpu %d: err=%d.\n",
- vc->vcpu_id, rc);
- return 0;
- }
-
- spin_lock_init(&vc->arch.hvm_vmx.vmcs_lock);
-
- if ( (io_bitmap_a = alloc_xenheap_pages(IO_BITMAP_ORDER)) == NULL )
- {
- DPRINTK("Failed to allocate io bitmap b for vcpu %d.\n",
- vc->vcpu_id);
- return 0;
- }
-
- if ( (io_bitmap_b = alloc_xenheap_pages(IO_BITMAP_ORDER)) == NULL )
- {
- DPRINTK("Failed to allocate io bitmap b for vcpu %d.\n",
- vc->vcpu_id);
- return 0;
- }
-
- memset(io_bitmap_a, 0xff, 0x1000);
- memset(io_bitmap_b, 0xff, 0x1000);
-
- /* don't bother debug port access */
- clear_bit(PC_DEBUG_PORT, io_bitmap_a);
-
- vc->arch.hvm_vmx.io_bitmap_a = io_bitmap_a;
- vc->arch.hvm_vmx.io_bitmap_b = io_bitmap_b;
-
+ dprintk(XENLOG_WARNING,
+ "Failed to create VMCS for vcpu %d: err=%d.\n",
+ v->vcpu_id, rc);
+ return rc;
}
- /*
- * Required to do this once per domain XXX todo: add a seperate function
- * to do these.
- */
- memset(&d->shared_info->evtchn_mask[0], 0xff,
- sizeof(d->shared_info->evtchn_mask));
-
- return 1;
+ return 0;
}
-static void vmx_relinquish_guest_resources(struct domain *d)
+static void vmx_vcpu_destroy(struct vcpu *v)
{
- struct vcpu *v;
-
- for_each_vcpu ( d, v )
- {
- vmx_destroy_vmcs(v);
- if ( !test_bit(_VCPUF_initialised, &v->vcpu_flags) )
- continue;
- kill_timer(&v->arch.hvm_vcpu.hlt_timer);
- if ( VLAPIC(v) != NULL )
- {
- kill_timer(&VLAPIC(v)->vlapic_timer);
- unmap_domain_page_global(VLAPIC(v)->regs);
- free_domheap_page(VLAPIC(v)->regs_page);
- xfree(VLAPIC(v));
- }
- hvm_release_assist_channel(v);
- }
-
- kill_timer(&d->arch.hvm_domain.pl_time.periodic_tm.timer);
- rtc_deinit(d);
- pmtimer_deinit(d);
-
- if ( d->arch.hvm_domain.shared_page_va )
- unmap_domain_page_global(
- (void *)d->arch.hvm_domain.shared_page_va);
-
- if ( d->arch.hvm_domain.buffered_io_va )
- unmap_domain_page_global((void *)d->arch.hvm_domain.buffered_io_va);
+ vmx_destroy_vmcs(v);
}
#ifdef __x86_64__
@@ -235,14 +154,14 @@ static inline int long_mode_do_msr_read(struct cpu_user_regs *regs)
/* XXX should it be GP fault */
domain_crash_synchronous();
- __vmread(GUEST_FS_BASE, &msr_content);
+ msr_content = __vmread(GUEST_FS_BASE);
break;
case MSR_GS_BASE:
if ( !(vmx_long_mode_enabled(v)) )
domain_crash_synchronous();
- __vmread(GUEST_GS_BASE, &msr_content);
+ msr_content = __vmread(GUEST_GS_BASE);
break;
case MSR_SHADOW_GS_BASE:
@@ -404,20 +323,20 @@ static inline int long_mode_do_msr_write(struct cpu_user_regs *regs)
static inline void vmx_save_dr(struct vcpu *v)
{
- if ( v->arch.hvm_vcpu.flag_dr_dirty )
- {
- savedebug(&v->arch.guest_context, 0);
- savedebug(&v->arch.guest_context, 1);
- savedebug(&v->arch.guest_context, 2);
- savedebug(&v->arch.guest_context, 3);
- savedebug(&v->arch.guest_context, 6);
-
- v->arch.hvm_vcpu.flag_dr_dirty = 0;
-
- v->arch.hvm_vcpu.u.vmx.exec_control |= CPU_BASED_MOV_DR_EXITING;
- __vmwrite(CPU_BASED_VM_EXEC_CONTROL,
- v->arch.hvm_vcpu.u.vmx.exec_control);
- }
+ if ( !v->arch.hvm_vcpu.flag_dr_dirty )
+ return;
+
+ /* Clear the DR dirty flag and re-enable intercepts for DR accesses. */
+ v->arch.hvm_vcpu.flag_dr_dirty = 0;
+ v->arch.hvm_vcpu.u.vmx.exec_control |= CPU_BASED_MOV_DR_EXITING;
+ __vmwrite(CPU_BASED_VM_EXEC_CONTROL, v->arch.hvm_vcpu.u.vmx.exec_control);
+
+ savedebug(&v->arch.guest_context, 0);
+ savedebug(&v->arch.guest_context, 1);
+ savedebug(&v->arch.guest_context, 2);
+ savedebug(&v->arch.guest_context, 3);
+ savedebug(&v->arch.guest_context, 6);
+ v->arch.guest_context.debugreg[7] = __vmread(GUEST_DR7);
}
static inline void __restore_debug_registers(struct vcpu *v)
@@ -428,7 +347,7 @@ static inline void __restore_debug_registers(struct vcpu *v)
loaddebug(&v->arch.guest_context, 3);
/* No 4 and 5 */
loaddebug(&v->arch.guest_context, 6);
- /* DR7 is loaded from the vmcs. */
+ /* DR7 is loaded from the VMCS. */
}
/*
@@ -436,31 +355,26 @@ static inline void __restore_debug_registers(struct vcpu *v)
* need to be restored if their value is going to affect execution -- i.e.,
* if one of the breakpoints is enabled. So mask out all bits that don't
* enable some breakpoint functionality.
- *
- * This is in part necessary because bit 10 of DR7 is hardwired to 1, so a
- * simple if( guest_dr7 ) will always return true. As long as we're masking,
- * we might as well do it right.
*/
#define DR7_ACTIVE_MASK 0xff
static inline void vmx_restore_dr(struct vcpu *v)
{
- unsigned long guest_dr7;
-
- __vmread(GUEST_DR7, &guest_dr7);
-
- /* Assumes guest does not have DR access at time of context switch. */
- if ( unlikely(guest_dr7 & DR7_ACTIVE_MASK) )
+ /* NB. __vmread() is not usable here, so we cannot read from the VMCS. */
+ if ( unlikely(v->arch.guest_context.debugreg[7] & DR7_ACTIVE_MASK) )
__restore_debug_registers(v);
}
static void vmx_freeze_time(struct vcpu *v)
{
struct periodic_time *pt=&v->domain->arch.hvm_domain.pl_time.periodic_tm;
-
- if ( pt->enabled && pt->first_injected && !v->arch.hvm_vcpu.guest_time ) {
+
+ if ( pt->enabled && pt->first_injected
+ && (v->vcpu_id == pt->bind_vcpu)
+ && !v->arch.hvm_vcpu.guest_time ) {
v->arch.hvm_vcpu.guest_time = hvm_get_guest_time(v);
- stop_timer(&(pt->timer));
+ if ( !test_bit(_VCPUF_blocked, &v->vcpu_flags) )
+ stop_timer(&pt->timer);
}
}
@@ -488,17 +402,15 @@ static void stop_vmx(void)
void vmx_migrate_timers(struct vcpu *v)
{
- struct periodic_time *pt = &(v->domain->arch.hvm_domain.pl_time.periodic_tm);
+ struct periodic_time *pt = &v->domain->arch.hvm_domain.pl_time.periodic_tm;
struct RTCState *vrtc = &v->domain->arch.hvm_domain.pl_time.vrtc;
struct PMTState *vpmt = &v->domain->arch.hvm_domain.pl_time.vpmt;
if ( pt->enabled )
{
migrate_timer(&pt->timer, v->processor);
- migrate_timer(&v->arch.hvm_vcpu.hlt_timer, v->processor);
}
- if ( VLAPIC(v) != NULL )
- migrate_timer(&VLAPIC(v)->vlapic_timer, v->processor);
+ migrate_timer(&vcpu_vlapic(v)->vlapic_timer, v->processor);
migrate_timer(&vrtc->second_timer, v->processor);
migrate_timer(&vrtc->second_timer2, v->processor);
migrate_timer(&vpmt->timer, v->processor);
@@ -511,23 +423,23 @@ static void vmx_store_cpu_guest_regs(
if ( regs != NULL )
{
- __vmread(GUEST_RFLAGS, &regs->eflags);
- __vmread(GUEST_SS_SELECTOR, &regs->ss);
- __vmread(GUEST_CS_SELECTOR, &regs->cs);
- __vmread(GUEST_DS_SELECTOR, &regs->ds);
- __vmread(GUEST_ES_SELECTOR, &regs->es);
- __vmread(GUEST_GS_SELECTOR, &regs->gs);
- __vmread(GUEST_FS_SELECTOR, &regs->fs);
- __vmread(GUEST_RIP, &regs->eip);
- __vmread(GUEST_RSP, &regs->esp);
+ regs->eflags = __vmread(GUEST_RFLAGS);
+ regs->ss = __vmread(GUEST_SS_SELECTOR);
+ regs->cs = __vmread(GUEST_CS_SELECTOR);
+ regs->ds = __vmread(GUEST_DS_SELECTOR);
+ regs->es = __vmread(GUEST_ES_SELECTOR);
+ regs->gs = __vmread(GUEST_GS_SELECTOR);
+ regs->fs = __vmread(GUEST_FS_SELECTOR);
+ regs->eip = __vmread(GUEST_RIP);
+ regs->esp = __vmread(GUEST_RSP);
}
if ( crs != NULL )
{
- __vmread(CR0_READ_SHADOW, &crs[0]);
+ crs[0] = v->arch.hvm_vmx.cpu_shadow_cr0;
crs[2] = v->arch.hvm_vmx.cpu_cr2;
- __vmread(GUEST_CR3, &crs[3]);
- __vmread(CR4_READ_SHADOW, &crs[4]);
+ crs[3] = __vmread(GUEST_CR3);
+ crs[4] = v->arch.hvm_vmx.cpu_shadow_cr4;
}
vmx_vmcs_exit(v);
@@ -547,29 +459,26 @@ static void vmx_store_cpu_guest_regs(
*/
static void fixup_vm86_seg_bases(struct cpu_user_regs *regs)
{
- int err = 0;
unsigned long base;
- err |= __vmread(GUEST_ES_BASE, &base);
+ base = __vmread(GUEST_ES_BASE);
if (regs->es << 4 != base)
- err |= __vmwrite(GUEST_ES_BASE, regs->es << 4);
- err |= __vmread(GUEST_CS_BASE, &base);
+ __vmwrite(GUEST_ES_BASE, regs->es << 4);
+ base = __vmread(GUEST_CS_BASE);
if (regs->cs << 4 != base)
- err |= __vmwrite(GUEST_CS_BASE, regs->cs << 4);
- err |= __vmread(GUEST_SS_BASE, &base);
+ __vmwrite(GUEST_CS_BASE, regs->cs << 4);
+ base = __vmread(GUEST_SS_BASE);
if (regs->ss << 4 != base)
- err |= __vmwrite(GUEST_SS_BASE, regs->ss << 4);
- err |= __vmread(GUEST_DS_BASE, &base);
+ __vmwrite(GUEST_SS_BASE, regs->ss << 4);
+ base = __vmread(GUEST_DS_BASE);
if (regs->ds << 4 != base)
- err |= __vmwrite(GUEST_DS_BASE, regs->ds << 4);
- err |= __vmread(GUEST_FS_BASE, &base);
+ __vmwrite(GUEST_DS_BASE, regs->ds << 4);
+ base = __vmread(GUEST_FS_BASE);
if (regs->fs << 4 != base)
- err |= __vmwrite(GUEST_FS_BASE, regs->fs << 4);
- err |= __vmread(GUEST_GS_BASE, &base);
+ __vmwrite(GUEST_FS_BASE, regs->fs << 4);
+ base = __vmread(GUEST_GS_BASE);
if (regs->gs << 4 != base)
- err |= __vmwrite(GUEST_GS_BASE, regs->gs << 4);
-
- BUG_ON(err);
+ __vmwrite(GUEST_GS_BASE, regs->gs << 4);
}
static void vmx_load_cpu_guest_regs(struct vcpu *v, struct cpu_user_regs *regs)
@@ -584,7 +493,8 @@ static void vmx_load_cpu_guest_regs(struct vcpu *v, struct cpu_user_regs *regs)
__vmwrite(GUEST_RSP, regs->esp);
- __vmwrite(GUEST_RFLAGS, regs->eflags);
+ /* NB. Bit 1 of RFLAGS must be set for VMENTRY to succeed. */
+ __vmwrite(GUEST_RFLAGS, regs->eflags | 2UL);
if (regs->eflags & EF_TF)
__vm_set_bit(EXCEPTION_BITMAP, EXCEPTION_BITMAP_DB);
else
@@ -616,13 +526,9 @@ static unsigned long vmx_get_ctrl_reg(struct vcpu *v, unsigned int num)
return 0; /* dummy */
}
-
-
/* Make sure that xen intercepts any FP accesses from current */
static void vmx_stts(struct vcpu *v)
{
- unsigned long cr0;
-
/* VMX depends on operating on the current vcpu */
ASSERT(v == current);
@@ -632,58 +538,31 @@ static void vmx_stts(struct vcpu *v)
* then this is not necessary: no FPU activity can occur until the guest
* clears CR0.TS, and we will initialise the FPU when that happens.
*/
- __vmread_vcpu(v, CR0_READ_SHADOW, &cr0);
- if ( !(cr0 & X86_CR0_TS) )
+ if ( !(v->arch.hvm_vmx.cpu_shadow_cr0 & X86_CR0_TS) )
{
- __vmread_vcpu(v, GUEST_CR0, &cr0);
- __vmwrite(GUEST_CR0, cr0 | X86_CR0_TS);
+ v->arch.hvm_vmx.cpu_cr0 |= X86_CR0_TS;
+ __vmwrite(GUEST_CR0, v->arch.hvm_vmx.cpu_cr0);
__vm_set_bit(EXCEPTION_BITMAP, EXCEPTION_BITMAP_NM);
}
}
-
static void vmx_set_tsc_offset(struct vcpu *v, u64 offset)
{
- /* VMX depends on operating on the current vcpu */
- ASSERT(v == current);
-
+ vmx_vmcs_enter(v);
__vmwrite(TSC_OFFSET, offset);
#if defined (__i386__)
__vmwrite(TSC_OFFSET_HIGH, offset >> 32);
#endif
+ vmx_vmcs_exit(v);
}
-
-
-/* SMP VMX guest support */
-static void vmx_init_ap_context(struct vcpu_guest_context *ctxt,
- int vcpuid, int trampoline_vector)
+static void vmx_init_ap_context(
+ struct vcpu_guest_context *ctxt, int vcpuid, int trampoline_vector)
{
- int i;
-
memset(ctxt, 0, sizeof(*ctxt));
-
- /*
- * Initial register values:
- */
ctxt->user_regs.eip = VMXASSIST_BASE;
ctxt->user_regs.edx = vcpuid;
ctxt->user_regs.ebx = trampoline_vector;
-
- ctxt->flags = VGCF_HVM_GUEST;
-
- /* Virtual IDT is empty at start-of-day. */
- for ( i = 0; i < 256; i++ )
- {
- ctxt->trap_ctxt[i].vector = i;
- ctxt->trap_ctxt[i].cs = FLAT_KERNEL_CS;
- }
-
- /* No callback handlers. */
-#if defined(__i386__)
- ctxt->event_callback_cs = FLAT_KERNEL_CS;
- ctxt->failsafe_callback_cs = FLAT_KERNEL_CS;
-#endif
}
void do_nmi(struct cpu_user_regs *);
@@ -716,7 +595,7 @@ static int vmx_realmode(struct vcpu *v)
ASSERT(v == current);
- __vmread(GUEST_RFLAGS, &rflags);
+ rflags = __vmread(GUEST_RFLAGS);
return rflags & X86_EFLAGS_VM;
}
@@ -726,7 +605,7 @@ static int vmx_guest_x86_mode(struct vcpu *v)
ASSERT(v == current);
- __vmread(GUEST_CS_AR_BYTES, &cs_ar_bytes);
+ cs_ar_bytes = __vmread(GUEST_CS_AR_BYTES);
if ( vmx_long_mode_enabled(v) )
return ((cs_ar_bytes & (1u<<13)) ?
@@ -739,6 +618,12 @@ static int vmx_guest_x86_mode(struct vcpu *v)
X86EMUL_MODE_PROT32 : X86EMUL_MODE_PROT16);
}
+static int vmx_pae_enabled(struct vcpu *v)
+{
+ unsigned long cr4 = v->arch.hvm_vmx.cpu_shadow_cr4;
+ return (vmx_paging_enabled(v) && (cr4 & X86_CR4_PAE));
+}
+
/* Setup HVM interfaces */
static void vmx_setup_hvm_funcs(void)
{
@@ -747,8 +632,8 @@ static void vmx_setup_hvm_funcs(void)
hvm_funcs.disable = stop_vmx;
- hvm_funcs.initialize_guest_resources = vmx_initialize_guest_resources;
- hvm_funcs.relinquish_guest_resources = vmx_relinquish_guest_resources;
+ hvm_funcs.vcpu_initialise = vmx_vcpu_initialise;
+ hvm_funcs.vcpu_destroy = vmx_vcpu_destroy;
hvm_funcs.store_cpu_guest_regs = vmx_store_cpu_guest_regs;
hvm_funcs.load_cpu_guest_regs = vmx_load_cpu_guest_regs;
@@ -840,7 +725,7 @@ int start_vmx(void)
static int __get_instruction_length(void)
{
int len;
- __vmread(VM_EXIT_INSTRUCTION_LEN, &len); /* Safe: callers audited */
+ len = __vmread(VM_EXIT_INSTRUCTION_LEN); /* Safe: callers audited */
if ( (len < 1) || (len > 15) )
__hvm_bug(guest_cpu_user_regs());
return len;
@@ -850,7 +735,7 @@ static void inline __update_guest_eip(unsigned long inst_len)
{
unsigned long current_eip;
- __vmread(GUEST_RIP, &current_eip);
+ current_eip = __vmread(GUEST_RIP);
__vmwrite(GUEST_RIP, current_eip + inst_len);
__vmwrite(GUEST_INTERRUPTIBILITY_INFO, 0);
}
@@ -863,8 +748,8 @@ static int vmx_do_page_fault(unsigned long va, struct cpu_user_regs *regs)
{
unsigned long eip, cs;
- __vmread(GUEST_CS_BASE, &cs);
- __vmread(GUEST_RIP, &eip);
+ cs = __vmread(GUEST_CS_BASE);
+ eip = __vmread(GUEST_RIP);
HVM_DBG_LOG(DBG_LEVEL_VMMU,
"vmx_do_page_fault = 0x%lx, cs_base=%lx, "
"eip = %lx, error_code = %lx\n",
@@ -878,7 +763,7 @@ static int vmx_do_page_fault(unsigned long va, struct cpu_user_regs *regs)
#if 0
if ( !result )
{
- __vmread(GUEST_RIP, &eip);
+ eip = __vmread(GUEST_RIP);
printk("vmx pgfault to guest va=%lx eip=%lx\n", va, eip);
}
#endif
@@ -888,19 +773,16 @@ static int vmx_do_page_fault(unsigned long va, struct cpu_user_regs *regs)
static void vmx_do_no_device_fault(void)
{
- unsigned long cr0;
struct vcpu *v = current;
setup_fpu(current);
__vm_clear_bit(EXCEPTION_BITMAP, EXCEPTION_BITMAP_NM);
/* Disable TS in guest CR0 unless the guest wants the exception too. */
- __vmread_vcpu(v, CR0_READ_SHADOW, &cr0);
- if ( !(cr0 & X86_CR0_TS) )
+ if ( !(v->arch.hvm_vmx.cpu_shadow_cr0 & X86_CR0_TS) )
{
- __vmread_vcpu(v, GUEST_CR0, &cr0);
- cr0 &= ~X86_CR0_TS;
- __vmwrite(GUEST_CR0, cr0);
+ v->arch.hvm_vmx.cpu_cr0 &= ~X86_CR0_TS;
+ __vmwrite(GUEST_CR0, v->arch.hvm_vmx.cpu_cr0);
}
}
@@ -913,7 +795,7 @@ static void vmx_do_cpuid(struct cpu_user_regs *regs)
unsigned long eip;
struct vcpu *v = current;
- __vmread(GUEST_RIP, &eip);
+ eip = __vmread(GUEST_RIP);
HVM_DBG_LOG(DBG_LEVEL_3, "(eax) 0x%08lx, (ebx) 0x%08lx, "
"(ecx) 0x%08lx, (edx) 0x%08lx, (esi) 0x%08lx, (edi) 0x%08lx",
@@ -936,7 +818,7 @@ static void vmx_do_cpuid(struct cpu_user_regs *regs)
unsigned long mfn = get_mfn_from_gpfn(value >> PAGE_SHIFT);
char *p;
- DPRINTK("Input address is 0x%"PRIx64".\n", value);
+ gdprintk(XENLOG_INFO, "Input address is 0x%"PRIx64".\n", value);
/* 8-byte aligned valid pseudophys address from vmxassist, please. */
if ( (value & 7) || (mfn == INVALID_MFN) ||
@@ -947,7 +829,7 @@ static void vmx_do_cpuid(struct cpu_user_regs *regs)
value = *((uint64_t *)(p + (value & (PAGE_SIZE - 1))));
unmap_domain_page(p);
- DPRINTK("Output value is 0x%"PRIx64".\n", value);
+ gdprintk(XENLOG_INFO, "Output value is 0x%"PRIx64".\n", value);
ecx = (u32)(value >> 0);
edx = (u32)(value >> 32);
}
@@ -957,17 +839,11 @@ static void vmx_do_cpuid(struct cpu_user_regs *regs)
if ( input == CPUID_LEAF_0x1 )
{
- /* mask off reserved bits */
+ /* Mask off reserved bits. */
ecx &= ~VMX_VCPU_CPUID_L1_ECX_RESERVED;
- if ( !hvm_apic_support(v->domain) ||
- !vlapic_global_enabled((VLAPIC(v))) )
- {
- /* Since the apic is disabled, avoid any
- confusion about SMP cpus being available */
-
+ if ( !vlapic_global_enabled(vcpu_vlapic(v)) )
clear_bit(X86_FEATURE_APIC, &edx);
- }
#if CONFIG_PAGING_LEVELS >= 3
if ( !v->domain->arch.hvm_domain.params[HVM_PARAM_PAE_ENABLED] )
@@ -1060,7 +936,7 @@ static void vmx_do_invlpg(unsigned long va)
unsigned long eip;
struct vcpu *v = current;
- __vmread(GUEST_RIP, &eip);
+ eip = __vmread(GUEST_RIP);
HVM_DBG_LOG(DBG_LEVEL_VMMU, "eip=%lx, va=%lx",
eip, va);
@@ -1083,7 +959,7 @@ static int check_for_null_selector(unsigned long eip, int inst_len, int dir)
/* INS can only use ES segment register, and it can't be overridden */
if ( dir == IOREQ_READ )
{
- __vmread(GUEST_ES_SELECTOR, &sel);
+ sel = __vmread(GUEST_ES_SELECTOR);
return sel == 0 ? 1 : 0;
}
@@ -1105,25 +981,25 @@ static int check_for_null_selector(unsigned long eip, int inst_len, int dir)
case 0x67: /* addr32 */
continue;
case 0x2e: /* CS */
- __vmread(GUEST_CS_SELECTOR, &sel);
+ sel = __vmread(GUEST_CS_SELECTOR);
break;
case 0x36: /* SS */
- __vmread(GUEST_SS_SELECTOR, &sel);
+ sel = __vmread(GUEST_SS_SELECTOR);
break;
case 0x26: /* ES */
- __vmread(GUEST_ES_SELECTOR, &sel);
+ sel = __vmread(GUEST_ES_SELECTOR);
break;
case 0x64: /* FS */
- __vmread(GUEST_FS_SELECTOR, &sel);
+ sel = __vmread(GUEST_FS_SELECTOR);
break;
case 0x65: /* GS */
- __vmread(GUEST_GS_SELECTOR, &sel);
+ sel = __vmread(GUEST_GS_SELECTOR);
break;
case 0x3e: /* DS */
/* FALLTHROUGH */
default:
/* DS is the default */
- __vmread(GUEST_DS_SELECTOR, &sel);
+ sel = __vmread(GUEST_DS_SELECTOR);
}
return sel == 0 ? 1 : 0;
}
@@ -1161,7 +1037,7 @@ static void vmx_io_instruction(unsigned long exit_qualification,
else
port = regs->edx & 0xffff;
- TRACE_VMEXIT(1,port);
+ TRACE_VMEXIT(1, port);
size = (exit_qualification & 7) + 1;
dir = test_bit(3, &exit_qualification); /* direction */
@@ -1170,7 +1046,7 @@ static void vmx_io_instruction(unsigned long exit_qualification,
unsigned long addr, count = 1;
int sign = regs->eflags & X86_EFLAGS_DF ? -1 : 1;
- __vmread(GUEST_LINEAR_ADDRESS, &addr);
+ addr = __vmread(GUEST_LINEAR_ADDRESS);
/*
* In protected mode, guest linear address is invalid if the
@@ -1225,102 +1101,104 @@ static void vmx_io_instruction(unsigned long exit_qualification,
if ( port == 0xe9 && dir == IOREQ_WRITE && size == 1 )
hvm_print_line(current, regs->eax); /* guest debug output */
+ if ( dir == IOREQ_WRITE )
+ TRACE_VMEXIT(2, regs->eax);
+
regs->eip += inst_len;
send_pio_req(port, 1, size, regs->eax, dir, df, 0);
}
}
-static int vmx_world_save(struct vcpu *v, struct vmx_assist_context *c)
+static void vmx_world_save(struct vcpu *v, struct vmx_assist_context *c)
{
- int error = 0;
-
/* NB. Skip transition instruction. */
- error |= __vmread(GUEST_RIP, &c->eip);
+ c->eip = __vmread(GUEST_RIP);
c->eip += __get_instruction_length(); /* Safe: MOV Cn, LMSW, CLTS */
- error |= __vmread(GUEST_RSP, &c->esp);
- error |= __vmread(GUEST_RFLAGS, &c->eflags);
+ c->esp = __vmread(GUEST_RSP);
+ c->eflags = __vmread(GUEST_RFLAGS);
- error |= __vmread(CR0_READ_SHADOW, &c->cr0);
+ c->cr0 = v->arch.hvm_vmx.cpu_shadow_cr0;
c->cr3 = v->arch.hvm_vmx.cpu_cr3;
- error |= __vmread(CR4_READ_SHADOW, &c->cr4);
-
- error |= __vmread(GUEST_IDTR_LIMIT, &c->idtr_limit);
- error |= __vmread(GUEST_IDTR_BASE, &c->idtr_base);
-
- error |= __vmread(GUEST_GDTR_LIMIT, &c->gdtr_limit);
- error |= __vmread(GUEST_GDTR_BASE, &c->gdtr_base);
-
- error |= __vmread(GUEST_CS_SELECTOR, &c->cs_sel);
- error |= __vmread(GUEST_CS_LIMIT, &c->cs_limit);
- error |= __vmread(GUEST_CS_BASE, &c->cs_base);
- error |= __vmread(GUEST_CS_AR_BYTES, &c->cs_arbytes.bytes);
-
- error |= __vmread(GUEST_DS_SELECTOR, &c->ds_sel);
- error |= __vmread(GUEST_DS_LIMIT, &c->ds_limit);
- error |= __vmread(GUEST_DS_BASE, &c->ds_base);
- error |= __vmread(GUEST_DS_AR_BYTES, &c->ds_arbytes.bytes);
-
- error |= __vmread(GUEST_ES_SELECTOR, &c->es_sel);
- error |= __vmread(GUEST_ES_LIMIT, &c->es_limit);
- error |= __vmread(GUEST_ES_BASE, &c->es_base);
- error |= __vmread(GUEST_ES_AR_BYTES, &c->es_arbytes.bytes);
-
- error |= __vmread(GUEST_SS_SELECTOR, &c->ss_sel);
- error |= __vmread(GUEST_SS_LIMIT, &c->ss_limit);
- error |= __vmread(GUEST_SS_BASE, &c->ss_base);
- error |= __vmread(GUEST_SS_AR_BYTES, &c->ss_arbytes.bytes);
-
- error |= __vmread(GUEST_FS_SELECTOR, &c->fs_sel);
- error |= __vmread(GUEST_FS_LIMIT, &c->fs_limit);
- error |= __vmread(GUEST_FS_BASE, &c->fs_base);
- error |= __vmread(GUEST_FS_AR_BYTES, &c->fs_arbytes.bytes);
-
- error |= __vmread(GUEST_GS_SELECTOR, &c->gs_sel);
- error |= __vmread(GUEST_GS_LIMIT, &c->gs_limit);
- error |= __vmread(GUEST_GS_BASE, &c->gs_base);
- error |= __vmread(GUEST_GS_AR_BYTES, &c->gs_arbytes.bytes);
-
- error |= __vmread(GUEST_TR_SELECTOR, &c->tr_sel);
- error |= __vmread(GUEST_TR_LIMIT, &c->tr_limit);
- error |= __vmread(GUEST_TR_BASE, &c->tr_base);
- error |= __vmread(GUEST_TR_AR_BYTES, &c->tr_arbytes.bytes);
-
- error |= __vmread(GUEST_LDTR_SELECTOR, &c->ldtr_sel);
- error |= __vmread(GUEST_LDTR_LIMIT, &c->ldtr_limit);
- error |= __vmread(GUEST_LDTR_BASE, &c->ldtr_base);
- error |= __vmread(GUEST_LDTR_AR_BYTES, &c->ldtr_arbytes.bytes);
-
- return !error;
+ c->cr4 = v->arch.hvm_vmx.cpu_shadow_cr4;
+
+ c->idtr_limit = __vmread(GUEST_IDTR_LIMIT);
+ c->idtr_base = __vmread(GUEST_IDTR_BASE);
+
+ c->gdtr_limit = __vmread(GUEST_GDTR_LIMIT);
+ c->gdtr_base = __vmread(GUEST_GDTR_BASE);
+
+ c->cs_sel = __vmread(GUEST_CS_SELECTOR);
+ c->cs_limit = __vmread(GUEST_CS_LIMIT);
+ c->cs_base = __vmread(GUEST_CS_BASE);
+ c->cs_arbytes.bytes = __vmread(GUEST_CS_AR_BYTES);
+
+ c->ds_sel = __vmread(GUEST_DS_SELECTOR);
+ c->ds_limit = __vmread(GUEST_DS_LIMIT);
+ c->ds_base = __vmread(GUEST_DS_BASE);
+ c->ds_arbytes.bytes = __vmread(GUEST_DS_AR_BYTES);
+
+ c->es_sel = __vmread(GUEST_ES_SELECTOR);
+ c->es_limit = __vmread(GUEST_ES_LIMIT);
+ c->es_base = __vmread(GUEST_ES_BASE);
+ c->es_arbytes.bytes = __vmread(GUEST_ES_AR_BYTES);
+
+ c->ss_sel = __vmread(GUEST_SS_SELECTOR);
+ c->ss_limit = __vmread(GUEST_SS_LIMIT);
+ c->ss_base = __vmread(GUEST_SS_BASE);
+ c->ss_arbytes.bytes = __vmread(GUEST_SS_AR_BYTES);
+
+ c->fs_sel = __vmread(GUEST_FS_SELECTOR);
+ c->fs_limit = __vmread(GUEST_FS_LIMIT);
+ c->fs_base = __vmread(GUEST_FS_BASE);
+ c->fs_arbytes.bytes = __vmread(GUEST_FS_AR_BYTES);
+
+ c->gs_sel = __vmread(GUEST_GS_SELECTOR);
+ c->gs_limit = __vmread(GUEST_GS_LIMIT);
+ c->gs_base = __vmread(GUEST_GS_BASE);
+ c->gs_arbytes.bytes = __vmread(GUEST_GS_AR_BYTES);
+
+ c->tr_sel = __vmread(GUEST_TR_SELECTOR);
+ c->tr_limit = __vmread(GUEST_TR_LIMIT);
+ c->tr_base = __vmread(GUEST_TR_BASE);
+ c->tr_arbytes.bytes = __vmread(GUEST_TR_AR_BYTES);
+
+ c->ldtr_sel = __vmread(GUEST_LDTR_SELECTOR);
+ c->ldtr_limit = __vmread(GUEST_LDTR_LIMIT);
+ c->ldtr_base = __vmread(GUEST_LDTR_BASE);
+ c->ldtr_arbytes.bytes = __vmread(GUEST_LDTR_AR_BYTES);
}
-static int vmx_world_restore(struct vcpu *v, struct vmx_assist_context *c)
+static void vmx_world_restore(struct vcpu *v, struct vmx_assist_context *c)
{
unsigned long mfn, old_base_mfn;
- int error = 0;
- error |= __vmwrite(GUEST_RIP, c->eip);
- error |= __vmwrite(GUEST_RSP, c->esp);
- error |= __vmwrite(GUEST_RFLAGS, c->eflags);
+ __vmwrite(GUEST_RIP, c->eip);
+ __vmwrite(GUEST_RSP, c->esp);
+ __vmwrite(GUEST_RFLAGS, c->eflags);
- error |= __vmwrite(CR0_READ_SHADOW, c->cr0);
+ v->arch.hvm_vmx.cpu_shadow_cr0 = c->cr0;
+ __vmwrite(CR0_READ_SHADOW, v->arch.hvm_vmx.cpu_shadow_cr0);
- if (!vmx_paging_enabled(v))
+ if ( !vmx_paging_enabled(v) )
goto skip_cr3;
- if (c->cr3 == v->arch.hvm_vmx.cpu_cr3) {
+ if ( c->cr3 == v->arch.hvm_vmx.cpu_cr3 )
+ {
/*
* This is simple TLB flush, implying the guest has
* removed some translation or changed page attributes.
* We simply invalidate the shadow.
*/
mfn = get_mfn_from_gpfn(c->cr3 >> PAGE_SHIFT);
- if (mfn != pagetable_get_pfn(v->arch.guest_table)) {
+ if ( mfn != pagetable_get_pfn(v->arch.guest_table) )
+ {
printk("Invalid CR3 value=%x", c->cr3);
domain_crash_synchronous();
- return 0;
}
- } else {
+ }
+ else
+ {
/*
* If different, make a shadow. Check if the PDBR is valid
* first.
@@ -1331,10 +1209,9 @@ static int vmx_world_restore(struct vcpu *v, struct vmx_assist_context *c)
{
printk("Invalid CR3 value=%x", c->cr3);
domain_crash_synchronous();
- return 0;
}
- if(!get_page(mfn_to_page(mfn), v->domain))
- return 0;
+ if ( !get_page(mfn_to_page(mfn), v->domain) )
+ domain_crash_synchronous();
old_base_mfn = pagetable_get_pfn(v->arch.guest_table);
v->arch.guest_table = pagetable_from_pfn(mfn);
if (old_base_mfn)
@@ -1346,65 +1223,63 @@ static int vmx_world_restore(struct vcpu *v, struct vmx_assist_context *c)
}
skip_cr3:
-
- if (!vmx_paging_enabled(v))
+ if ( !vmx_paging_enabled(v) )
HVM_DBG_LOG(DBG_LEVEL_VMMU, "switching to vmxassist. use phys table");
else
HVM_DBG_LOG(DBG_LEVEL_VMMU, "Update CR3 value = %x", c->cr3);
- error |= __vmwrite(GUEST_CR4, (c->cr4 | VMX_CR4_HOST_MASK));
- error |= __vmwrite(CR4_READ_SHADOW, c->cr4);
+ __vmwrite(GUEST_CR4, (c->cr4 | VMX_CR4_HOST_MASK));
+ v->arch.hvm_vmx.cpu_shadow_cr4 = c->cr4;
+ __vmwrite(CR4_READ_SHADOW, v->arch.hvm_vmx.cpu_shadow_cr4);
- error |= __vmwrite(GUEST_IDTR_LIMIT, c->idtr_limit);
- error |= __vmwrite(GUEST_IDTR_BASE, c->idtr_base);
+ __vmwrite(GUEST_IDTR_LIMIT, c->idtr_limit);
+ __vmwrite(GUEST_IDTR_BASE, c->idtr_base);
- error |= __vmwrite(GUEST_GDTR_LIMIT, c->gdtr_limit);
- error |= __vmwrite(GUEST_GDTR_BASE, c->gdtr_base);
+ __vmwrite(GUEST_GDTR_LIMIT, c->gdtr_limit);
+ __vmwrite(GUEST_GDTR_BASE, c->gdtr_base);
- error |= __vmwrite(GUEST_CS_SELECTOR, c->cs_sel);
- error |= __vmwrite(GUEST_CS_LIMIT, c->cs_limit);
- error |= __vmwrite(GUEST_CS_BASE, c->cs_base);
- error |= __vmwrite(GUEST_CS_AR_BYTES, c->cs_arbytes.bytes);
+ __vmwrite(GUEST_CS_SELECTOR, c->cs_sel);
+ __vmwrite(GUEST_CS_LIMIT, c->cs_limit);
+ __vmwrite(GUEST_CS_BASE, c->cs_base);
+ __vmwrite(GUEST_CS_AR_BYTES, c->cs_arbytes.bytes);
- error |= __vmwrite(GUEST_DS_SELECTOR, c->ds_sel);
- error |= __vmwrite(GUEST_DS_LIMIT, c->ds_limit);
- error |= __vmwrite(GUEST_DS_BASE, c->ds_base);
- error |= __vmwrite(GUEST_DS_AR_BYTES, c->ds_arbytes.bytes);
+ __vmwrite(GUEST_DS_SELECTOR, c->ds_sel);
+ __vmwrite(GUEST_DS_LIMIT, c->ds_limit);
+ __vmwrite(GUEST_DS_BASE, c->ds_base);
+ __vmwrite(GUEST_DS_AR_BYTES, c->ds_arbytes.bytes);
- error |= __vmwrite(GUEST_ES_SELECTOR, c->es_sel);
- error |= __vmwrite(GUEST_ES_LIMIT, c->es_limit);
- error |= __vmwrite(GUEST_ES_BASE, c->es_base);
- error |= __vmwrite(GUEST_ES_AR_BYTES, c->es_arbytes.bytes);
+ __vmwrite(GUEST_ES_SELECTOR, c->es_sel);
+ __vmwrite(GUEST_ES_LIMIT, c->es_limit);
+ __vmwrite(GUEST_ES_BASE, c->es_base);
+ __vmwrite(GUEST_ES_AR_BYTES, c->es_arbytes.bytes);
- error |= __vmwrite(GUEST_SS_SELECTOR, c->ss_sel);
- error |= __vmwrite(GUEST_SS_LIMIT, c->ss_limit);
- error |= __vmwrite(GUEST_SS_BASE, c->ss_base);
- error |= __vmwrite(GUEST_SS_AR_BYTES, c->ss_arbytes.bytes);
+ __vmwrite(GUEST_SS_SELECTOR, c->ss_sel);
+ __vmwrite(GUEST_SS_LIMIT, c->ss_limit);
+ __vmwrite(GUEST_SS_BASE, c->ss_base);
+ __vmwrite(GUEST_SS_AR_BYTES, c->ss_arbytes.bytes);
- error |= __vmwrite(GUEST_FS_SELECTOR, c->fs_sel);
- error |= __vmwrite(GUEST_FS_LIMIT, c->fs_limit);
- error |= __vmwrite(GUEST_FS_BASE, c->fs_base);
- error |= __vmwrite(GUEST_FS_AR_BYTES, c->fs_arbytes.bytes);
+ __vmwrite(GUEST_FS_SELECTOR, c->fs_sel);
+ __vmwrite(GUEST_FS_LIMIT, c->fs_limit);
+ __vmwrite(GUEST_FS_BASE, c->fs_base);
+ __vmwrite(GUEST_FS_AR_BYTES, c->fs_arbytes.bytes);
- error |= __vmwrite(GUEST_GS_SELECTOR, c->gs_sel);
- error |= __vmwrite(GUEST_GS_LIMIT, c->gs_limit);
- error |= __vmwrite(GUEST_GS_BASE, c->gs_base);
- error |= __vmwrite(GUEST_GS_AR_BYTES, c->gs_arbytes.bytes);
+ __vmwrite(GUEST_GS_SELECTOR, c->gs_sel);
+ __vmwrite(GUEST_GS_LIMIT, c->gs_limit);
+ __vmwrite(GUEST_GS_BASE, c->gs_base);
+ __vmwrite(GUEST_GS_AR_BYTES, c->gs_arbytes.bytes);
- error |= __vmwrite(GUEST_TR_SELECTOR, c->tr_sel);
- error |= __vmwrite(GUEST_TR_LIMIT, c->tr_limit);
- error |= __vmwrite(GUEST_TR_BASE, c->tr_base);
- error |= __vmwrite(GUEST_TR_AR_BYTES, c->tr_arbytes.bytes);
+ __vmwrite(GUEST_TR_SELECTOR, c->tr_sel);
+ __vmwrite(GUEST_TR_LIMIT, c->tr_limit);
+ __vmwrite(GUEST_TR_BASE, c->tr_base);
+ __vmwrite(GUEST_TR_AR_BYTES, c->tr_arbytes.bytes);
- error |= __vmwrite(GUEST_LDTR_SELECTOR, c->ldtr_sel);
- error |= __vmwrite(GUEST_LDTR_LIMIT, c->ldtr_limit);
- error |= __vmwrite(GUEST_LDTR_BASE, c->ldtr_base);
- error |= __vmwrite(GUEST_LDTR_AR_BYTES, c->ldtr_arbytes.bytes);
+ __vmwrite(GUEST_LDTR_SELECTOR, c->ldtr_sel);
+ __vmwrite(GUEST_LDTR_LIMIT, c->ldtr_limit);
+ __vmwrite(GUEST_LDTR_BASE, c->ldtr_base);
+ __vmwrite(GUEST_LDTR_AR_BYTES, c->ldtr_arbytes.bytes);
shadow_update_paging_modes(v);
__vmwrite(GUEST_CR3, v->arch.hvm_vcpu.hw_cr3);
-
- return !error;
}
enum { VMX_ASSIST_INVOKE = 0, VMX_ASSIST_RESTORE };
@@ -1434,8 +1309,7 @@ static int vmx_assist(struct vcpu *v, int mode)
if (hvm_copy_from_guest_phys(&cp, VMXASSIST_OLD_CONTEXT, sizeof(cp)))
goto error;
if (cp != 0) {
- if (!vmx_world_save(v, &c))
- goto error;
+ vmx_world_save(v, &c);
if (hvm_copy_to_guest_phys(cp, &c, sizeof(c)))
goto error;
}
@@ -1446,8 +1320,7 @@ static int vmx_assist(struct vcpu *v, int mode)
if (cp != 0) {
if (hvm_copy_from_guest_phys(&c, cp, sizeof(c)))
goto error;
- if (!vmx_world_restore(v, &c))
- goto error;
+ vmx_world_restore(v, &c);
v->arch.hvm_vmx.vmxassist_enabled = 1;
return 1;
}
@@ -1464,8 +1337,7 @@ static int vmx_assist(struct vcpu *v, int mode)
if (cp != 0) {
if (hvm_copy_from_guest_phys(&c, cp, sizeof(c)))
goto error;
- if (!vmx_world_restore(v, &c))
- goto error;
+ vmx_world_restore(v, &c);
v->arch.hvm_vmx.vmxassist_enabled = 0;
return 1;
}
@@ -1491,7 +1363,7 @@ static int vmx_set_cr0(unsigned long value)
/*
* CR0: We don't want to lose PE and PG.
*/
- __vmread_vcpu(v, CR0_READ_SHADOW, &old_cr0);
+ old_cr0 = v->arch.hvm_vmx.cpu_shadow_cr0;
paging_enabled = (old_cr0 & X86_CR0_PE) && (old_cr0 & X86_CR0_PG);
/* TS cleared? Then initialise FPU now. */
@@ -1501,8 +1373,11 @@ static int vmx_set_cr0(unsigned long value)
__vm_clear_bit(EXCEPTION_BITMAP, EXCEPTION_BITMAP_NM);
}
- __vmwrite(GUEST_CR0, value | X86_CR0_PE | X86_CR0_PG | X86_CR0_NE);
- __vmwrite(CR0_READ_SHADOW, value);
+ v->arch.hvm_vmx.cpu_cr0 = value | X86_CR0_PE | X86_CR0_PG | X86_CR0_NE;
+ __vmwrite(GUEST_CR0, v->arch.hvm_vmx.cpu_cr0);
+
+ v->arch.hvm_vmx.cpu_shadow_cr0 = value;
+ __vmwrite(CR0_READ_SHADOW, v->arch.hvm_vmx.cpu_shadow_cr0);
HVM_DBG_LOG(DBG_LEVEL_VMMU, "Update CR0 value = %lx\n", value);
@@ -1534,7 +1409,7 @@ static int vmx_set_cr0(unsigned long value)
HVM_DBG_LOG(DBG_LEVEL_1, "Enabling long mode\n");
v->arch.hvm_vmx.msr_content.msr_items[VMX_INDEX_MSR_EFER]
|= EFER_LMA;
- __vmread(VM_ENTRY_CONTROLS, &vm_entry_value);
+ vm_entry_value = __vmread(VM_ENTRY_CONTROLS);
vm_entry_value |= VM_ENTRY_IA32E_MODE;
__vmwrite(VM_ENTRY_CONTROLS, vm_entry_value);
}
@@ -1588,7 +1463,7 @@ static int vmx_set_cr0(unsigned long value)
{
v->arch.hvm_vmx.msr_content.msr_items[VMX_INDEX_MSR_EFER]
&= ~EFER_LMA;
- __vmread(VM_ENTRY_CONTROLS, &vm_entry_value);
+ vm_entry_value = __vmread(VM_ENTRY_CONTROLS);
vm_entry_value &= ~VM_ENTRY_IA32E_MODE;
__vmwrite(VM_ENTRY_CONTROLS, vm_entry_value);
}
@@ -1596,7 +1471,7 @@ static int vmx_set_cr0(unsigned long value)
if ( vmx_assist(v, VMX_ASSIST_INVOKE) )
{
- __vmread(GUEST_RIP, &eip);
+ eip = __vmread(GUEST_RIP);
HVM_DBG_LOG(DBG_LEVEL_1,
"Transfering control to vmxassist %%eip 0x%lx\n", eip);
return 0; /* do not update eip! */
@@ -1604,12 +1479,12 @@ static int vmx_set_cr0(unsigned long value)
}
else if ( v->arch.hvm_vmx.vmxassist_enabled )
{
- __vmread(GUEST_RIP, &eip);
+ eip = __vmread(GUEST_RIP);
HVM_DBG_LOG(DBG_LEVEL_1,
"Enabling CR0.PE at %%eip 0x%lx\n", eip);
if ( vmx_assist(v, VMX_ASSIST_RESTORE) )
{
- __vmread(GUEST_RIP, &eip);
+ eip = __vmread(GUEST_RIP);
HVM_DBG_LOG(DBG_LEVEL_1,
"Restoring to %%eip 0x%lx\n", eip);
return 0; /* do not update eip! */
@@ -1617,6 +1492,14 @@ static int vmx_set_cr0(unsigned long value)
}
else if ( (value & (X86_CR0_PE | X86_CR0_PG)) == X86_CR0_PE )
{
+ if ( vmx_long_mode_enabled(v) )
+ {
+ v->arch.hvm_vmx.msr_content.msr_items[VMX_INDEX_MSR_EFER]
+ &= ~EFER_LMA;
+ vm_entry_value = __vmread(VM_ENTRY_CONTROLS);
+ vm_entry_value &= ~VM_ENTRY_IA32E_MODE;
+ __vmwrite(VM_ENTRY_CONTROLS, vm_entry_value);
+ }
shadow_update_paging_modes(v);
__vmwrite(GUEST_CR3, v->arch.hvm_vcpu.hw_cr3);
}
@@ -1656,7 +1539,7 @@ static int mov_to_cr(int gp, int cr, struct cpu_user_regs *regs)
unsigned long value;
unsigned long old_cr;
struct vcpu *v = current;
- struct vlapic *vlapic = VLAPIC(v);
+ struct vlapic *vlapic = vcpu_vlapic(v);
switch ( gp ) {
CASE_GET_REG(EAX, eax);
@@ -1668,7 +1551,7 @@ static int mov_to_cr(int gp, int cr, struct cpu_user_regs *regs)
CASE_GET_REG(EDI, edi);
CASE_EXTEND_GET_REG;
case REG_ESP:
- __vmread(GUEST_RSP, &value);
+ value = __vmread(GUEST_RSP);
break;
default:
printk("invalid gp: %d\n", gp);
@@ -1738,9 +1621,9 @@ static int mov_to_cr(int gp, int cr, struct cpu_user_regs *regs)
}
case 4: /* CR4 */
{
- __vmread(CR4_READ_SHADOW, &old_cr);
+ old_cr = v->arch.hvm_vmx.cpu_shadow_cr4;
- if ( value & X86_CR4_PAE && !(old_cr & X86_CR4_PAE) )
+ if ( (value & X86_CR4_PAE) && !(old_cr & X86_CR4_PAE) )
{
if ( vmx_pgbit_test(v) )
{
@@ -1789,7 +1672,8 @@ static int mov_to_cr(int gp, int cr, struct cpu_user_regs *regs)
}
__vmwrite(GUEST_CR4, value| VMX_CR4_HOST_MASK);
- __vmwrite(CR4_READ_SHADOW, value);
+ v->arch.hvm_vmx.cpu_shadow_cr4 = value;
+ __vmwrite(CR4_READ_SHADOW, v->arch.hvm_vmx.cpu_shadow_cr4);
/*
* Writing to CR4 to modify the PSE, PGE, or PAE flag invalidates
@@ -1801,10 +1685,7 @@ static int mov_to_cr(int gp, int cr, struct cpu_user_regs *regs)
}
case 8:
{
- if ( vlapic == NULL )
- break;
vlapic_set_reg(vlapic, APIC_TASKPRI, ((value & 0x0F) << 4));
- vlapic_update_ppr(vlapic);
break;
}
default:
@@ -1822,7 +1703,7 @@ static void mov_from_cr(int cr, int gp, struct cpu_user_regs *regs)
{
unsigned long value = 0;
struct vcpu *v = current;
- struct vlapic *vlapic = VLAPIC(v);
+ struct vlapic *vlapic = vcpu_vlapic(v);
switch ( cr )
{
@@ -1830,8 +1711,6 @@ static void mov_from_cr(int cr, int gp, struct cpu_user_regs *regs)
value = (unsigned long)v->arch.hvm_vmx.cpu_cr3;
break;
case 8:
- if ( vlapic == NULL )
- break;
value = (unsigned long)vlapic_get_reg(vlapic, APIC_TASKPRI);
value = (value & 0xF0) >> 4;
break;
@@ -1888,16 +1767,14 @@ static int vmx_cr_access(unsigned long exit_qualification,
setup_fpu(v);
__vm_clear_bit(EXCEPTION_BITMAP, EXCEPTION_BITMAP_NM);
- __vmread_vcpu(v, GUEST_CR0, &value);
- value &= ~X86_CR0_TS; /* clear TS */
- __vmwrite(GUEST_CR0, value);
+ v->arch.hvm_vmx.cpu_cr0 &= ~X86_CR0_TS; /* clear TS */
+ __vmwrite(GUEST_CR0, v->arch.hvm_vmx.cpu_cr0);
- __vmread_vcpu(v, CR0_READ_SHADOW, &value);
- value &= ~X86_CR0_TS; /* clear TS */
- __vmwrite(CR0_READ_SHADOW, value);
+ v->arch.hvm_vmx.cpu_shadow_cr0 &= ~X86_CR0_TS; /* clear TS */
+ __vmwrite(CR0_READ_SHADOW, v->arch.hvm_vmx.cpu_shadow_cr0);
break;
case TYPE_LMSW:
- __vmread_vcpu(v, CR0_READ_SHADOW, &value);
+ value = v->arch.hvm_vmx.cpu_shadow_cr0;
value = (value & ~0xF) |
(((exit_qualification & LMSW_SOURCE_DATA) >> 16) & 0xF);
TRACE_VMEXIT(1, TYPE_LMSW);
@@ -1925,16 +1802,16 @@ static inline void vmx_do_msr_read(struct cpu_user_regs *regs)
msr_content = hvm_get_guest_time(v);
break;
case MSR_IA32_SYSENTER_CS:
- __vmread(GUEST_SYSENTER_CS, (u32 *)&msr_content);
+ msr_content = (u32)__vmread(GUEST_SYSENTER_CS);
break;
case MSR_IA32_SYSENTER_ESP:
- __vmread(GUEST_SYSENTER_ESP, &msr_content);
+ msr_content = __vmread(GUEST_SYSENTER_ESP);
break;
case MSR_IA32_SYSENTER_EIP:
- __vmread(GUEST_SYSENTER_EIP, &msr_content);
+ msr_content = __vmread(GUEST_SYSENTER_EIP);
break;
case MSR_IA32_APICBASE:
- msr_content = VLAPIC(v) ? VLAPIC(v)->apic_base_msr : 0;
+ msr_content = vcpu_vlapic(v)->apic_base_msr;
break;
default:
if (long_mode_do_msr_read(regs))
@@ -1972,6 +1849,13 @@ static inline void vmx_do_msr_write(struct cpu_user_regs *regs)
switch (regs->ecx) {
case MSR_IA32_TIME_STAMP_COUNTER:
+ {
+ struct periodic_time *pt =
+ &(v->domain->arch.hvm_domain.pl_time.periodic_tm);
+ if ( pt->enabled && pt->first_injected
+ && v->vcpu_id == pt->bind_vcpu )
+ pt->first_injected = 0;
+ }
hvm_set_guest_time(v, msr_content);
break;
case MSR_IA32_SYSENTER_CS:
@@ -1984,7 +1868,7 @@ static inline void vmx_do_msr_write(struct cpu_user_regs *regs)
__vmwrite(GUEST_SYSENTER_EIP, msr_content);
break;
case MSR_IA32_APICBASE:
- vlapic_msr_set(VLAPIC(v), msr_content);
+ vlapic_msr_set(vcpu_vlapic(v), msr_content);
break;
default:
if ( !long_mode_do_msr_write(regs) )
@@ -2000,14 +1884,13 @@ static inline void vmx_do_msr_write(struct cpu_user_regs *regs)
static void vmx_do_hlt(void)
{
unsigned long rflags;
- __vmread(GUEST_RFLAGS, &rflags);
+ rflags = __vmread(GUEST_RFLAGS);
hvm_hlt(rflags);
}
static inline void vmx_do_extint(struct cpu_user_regs *regs)
{
unsigned int vector;
- int error;
asmlinkage void do_IRQ(struct cpu_user_regs *);
fastcall void smp_apic_timer_interrupt(struct cpu_user_regs *);
@@ -2020,9 +1903,8 @@ static inline void vmx_do_extint(struct cpu_user_regs *regs)
fastcall void smp_thermal_interrupt(struct cpu_user_regs *regs);
#endif
- if ((error = __vmread(VM_EXIT_INTR_INFO, &vector))
- && !(vector & INTR_INFO_VALID_MASK))
- __hvm_bug(regs);
+ vector = __vmread(VM_EXIT_INTR_INFO);
+ BUG_ON(!(vector & INTR_INFO_VALID_MASK));
vector &= INTR_INFO_VECTOR_MASK;
TRACE_VMEXIT(1, vector);
@@ -2061,40 +1943,40 @@ static inline void vmx_do_extint(struct cpu_user_regs *regs)
#if defined (__x86_64__)
void store_cpu_user_regs(struct cpu_user_regs *regs)
{
- __vmread(GUEST_SS_SELECTOR, &regs->ss);
- __vmread(GUEST_RSP, &regs->rsp);
- __vmread(GUEST_RFLAGS, &regs->rflags);
- __vmread(GUEST_CS_SELECTOR, &regs->cs);
- __vmread(GUEST_DS_SELECTOR, &regs->ds);
- __vmread(GUEST_ES_SELECTOR, &regs->es);
- __vmread(GUEST_RIP, &regs->rip);
+ regs->ss = __vmread(GUEST_SS_SELECTOR);
+ regs->rsp = __vmread(GUEST_RSP);
+ regs->rflags = __vmread(GUEST_RFLAGS);
+ regs->cs = __vmread(GUEST_CS_SELECTOR);
+ regs->ds = __vmread(GUEST_DS_SELECTOR);
+ regs->es = __vmread(GUEST_ES_SELECTOR);
+ regs->rip = __vmread(GUEST_RIP);
}
#elif defined (__i386__)
void store_cpu_user_regs(struct cpu_user_regs *regs)
{
- __vmread(GUEST_SS_SELECTOR, &regs->ss);
- __vmread(GUEST_RSP, &regs->esp);
- __vmread(GUEST_RFLAGS, &regs->eflags);
- __vmread(GUEST_CS_SELECTOR, &regs->cs);
- __vmread(GUEST_DS_SELECTOR, &regs->ds);
- __vmread(GUEST_ES_SELECTOR, &regs->es);
- __vmread(GUEST_RIP, &regs->eip);
+ regs->ss = __vmread(GUEST_SS_SELECTOR);
+ regs->esp = __vmread(GUEST_RSP);
+ regs->eflags = __vmread(GUEST_RFLAGS);
+ regs->cs = __vmread(GUEST_CS_SELECTOR);
+ regs->ds = __vmread(GUEST_DS_SELECTOR);
+ regs->es = __vmread(GUEST_ES_SELECTOR);
+ regs->eip = __vmread(GUEST_RIP);
}
#endif
#ifdef XEN_DEBUGGER
void save_cpu_user_regs(struct cpu_user_regs *regs)
{
- __vmread(GUEST_SS_SELECTOR, &regs->xss);
- __vmread(GUEST_RSP, &regs->esp);
- __vmread(GUEST_RFLAGS, &regs->eflags);
- __vmread(GUEST_CS_SELECTOR, &regs->xcs);
- __vmread(GUEST_RIP, &regs->eip);
-
- __vmread(GUEST_GS_SELECTOR, &regs->xgs);
- __vmread(GUEST_FS_SELECTOR, &regs->xfs);
- __vmread(GUEST_ES_SELECTOR, &regs->xes);
- __vmread(GUEST_DS_SELECTOR, &regs->xds);
+ regs->xss = __vmread(GUEST_SS_SELECTOR);
+ regs->esp = __vmread(GUEST_RSP);
+ regs->eflags = __vmread(GUEST_RFLAGS);
+ regs->xcs = __vmread(GUEST_CS_SELECTOR);
+ regs->eip = __vmread(GUEST_RIP);
+
+ regs->xgs = __vmread(GUEST_GS_SELECTOR);
+ regs->xfs = __vmread(GUEST_FS_SELECTOR);
+ regs->xes = __vmread(GUEST_ES_SELECTOR);
+ regs->xds = __vmread(GUEST_DS_SELECTOR);
}
void restore_cpu_user_regs(struct cpu_user_regs *regs)
@@ -2116,10 +1998,10 @@ static void vmx_reflect_exception(struct vcpu *v)
{
int error_code, intr_info, vector;
- __vmread(VM_EXIT_INTR_INFO, &intr_info);
+ intr_info = __vmread(VM_EXIT_INTR_INFO);
vector = intr_info & 0xff;
if ( intr_info & INTR_INFO_DELIVER_CODE_MASK )
- __vmread(VM_EXIT_INTR_ERROR_CODE, &error_code);
+ error_code = __vmread(VM_EXIT_INTR_ERROR_CODE);
else
error_code = VMX_DELIVER_NO_ERROR_CODE;
@@ -2127,7 +2009,7 @@ static void vmx_reflect_exception(struct vcpu *v)
{
unsigned long rip;
- __vmread(GUEST_RIP, &rip);
+ rip = __vmread(GUEST_RIP);
HVM_DBG_LOG(DBG_LEVEL_1, "rip = %lx, error_code = %x",
rip, error_code);
}
@@ -2159,7 +2041,7 @@ asmlinkage void vmx_vmexit_handler(struct cpu_user_regs *regs)
unsigned long exit_qualification, inst_len = 0;
struct vcpu *v = current;
- __vmread(VM_EXIT_REASON, &exit_reason);
+ exit_reason = __vmread(VM_EXIT_REASON);
perfc_incra(vmexits, exit_reason);
@@ -2175,7 +2057,7 @@ asmlinkage void vmx_vmexit_handler(struct cpu_user_regs *regs)
{
unsigned int failed_vmentry_reason = exit_reason & 0xFFFF;
- __vmread(EXIT_QUALIFICATION, &exit_qualification);
+ exit_qualification = __vmread(EXIT_QUALIFICATION);
printk("Failed vm entry (exit reason 0x%x) ", exit_reason);
switch ( failed_vmentry_reason ) {
case EXIT_REASON_INVALID_GUEST_STATE:
@@ -2209,17 +2091,18 @@ asmlinkage void vmx_vmexit_handler(struct cpu_user_regs *regs)
* (1) We can get an exception (e.g. #PG) in the guest, or
* (2) NMI
*/
- unsigned int vector;
+ unsigned int intr_info, vector;
- if ( __vmread(VM_EXIT_INTR_INFO, &vector) ||
- !(vector & INTR_INFO_VALID_MASK) )
- domain_crash_synchronous();
- vector &= INTR_INFO_VECTOR_MASK;
+ intr_info = __vmread(VM_EXIT_INTR_INFO);
+ BUG_ON(!(intr_info & INTR_INFO_VALID_MASK));
+
+ vector = intr_info & INTR_INFO_VECTOR_MASK;
TRACE_VMEXIT(1, vector);
perfc_incra(cause_vector, vector);
- switch ( vector ) {
+ switch ( vector )
+ {
#ifdef XEN_DEBUGGER
case TRAP_debug:
{
@@ -2272,8 +2155,8 @@ asmlinkage void vmx_vmexit_handler(struct cpu_user_regs *regs)
}
case TRAP_page_fault:
{
- __vmread(EXIT_QUALIFICATION, &exit_qualification);
- __vmread(VM_EXIT_INTR_ERROR_CODE, &regs->error_code);
+ exit_qualification = __vmread(EXIT_QUALIFICATION);
+ regs->error_code = __vmread(VM_EXIT_INTR_ERROR_CODE);
TRACE_VMEXIT(3, regs->error_code);
TRACE_VMEXIT(4, exit_qualification);
@@ -2295,7 +2178,10 @@ asmlinkage void vmx_vmexit_handler(struct cpu_user_regs *regs)
break;
}
case TRAP_nmi:
- do_nmi(regs);
+ if ( (intr_info & INTR_INFO_INTR_TYPE_MASK) == INTR_TYPE_NMI )
+ do_nmi(regs); /* Real NMI, vector 2: normal processing. */
+ else
+ vmx_reflect_exception(v);
break;
default:
vmx_reflect_exception(v);
@@ -2332,7 +2218,7 @@ asmlinkage void vmx_vmexit_handler(struct cpu_user_regs *regs)
{
inst_len = __get_instruction_length(); /* Safe: INVLPG */
__update_guest_eip(inst_len);
- __vmread(EXIT_QUALIFICATION, &exit_qualification);
+ exit_qualification = __vmread(EXIT_QUALIFICATION);
vmx_do_invlpg(exit_qualification);
TRACE_VMEXIT(4, exit_qualification);
break;
@@ -2346,7 +2232,7 @@ asmlinkage void vmx_vmexit_handler(struct cpu_user_regs *regs)
}
case EXIT_REASON_CR_ACCESS:
{
- __vmread(EXIT_QUALIFICATION, &exit_qualification);
+ exit_qualification = __vmread(EXIT_QUALIFICATION);
inst_len = __get_instruction_length(); /* Safe: MOV Cn, LMSW, CLTS */
if ( vmx_cr_access(exit_qualification, regs) )
__update_guest_eip(inst_len);
@@ -2354,11 +2240,11 @@ asmlinkage void vmx_vmexit_handler(struct cpu_user_regs *regs)
break;
}
case EXIT_REASON_DR_ACCESS:
- __vmread(EXIT_QUALIFICATION, &exit_qualification);
+ exit_qualification = __vmread(EXIT_QUALIFICATION);
vmx_dr_access(exit_qualification, regs);
break;
case EXIT_REASON_IO_INSTRUCTION:
- __vmread(EXIT_QUALIFICATION, &exit_qualification);
+ exit_qualification = __vmread(EXIT_QUALIFICATION);
inst_len = __get_instruction_length(); /* Safe: IN, INS, OUT, OUTS */
vmx_io_instruction(exit_qualification, inst_len);
TRACE_VMEXIT(4, exit_qualification);
@@ -2398,27 +2284,24 @@ asmlinkage void vmx_vmexit_handler(struct cpu_user_regs *regs)
vmx_inject_hw_exception(v, TRAP_invalid_op, VMX_DELIVER_NO_ERROR_CODE);
break;
+ case EXIT_REASON_TPR_BELOW_THRESHOLD:
+ vcpu_vlapic(v)->flush_tpr_threshold = 1;
+ break;
+
default:
domain_crash_synchronous(); /* should not happen */
}
}
-asmlinkage void vmx_load_cr2(void)
-{
- struct vcpu *v = current;
-
- local_irq_disable();
- asm volatile("mov %0,%%cr2": :"r" (v->arch.hvm_vmx.cpu_cr2));
-}
-
asmlinkage void vmx_trace_vmentry(void)
{
+ struct vcpu *v = current;
TRACE_5D(TRC_VMX_VMENTRY + current->vcpu_id,
- this_cpu(trace_values)[0],
- this_cpu(trace_values)[1],
- this_cpu(trace_values)[2],
- this_cpu(trace_values)[3],
- this_cpu(trace_values)[4]);
+ v->arch.hvm_vcpu.hvm_trace_values[0],
+ v->arch.hvm_vcpu.hvm_trace_values[1],
+ v->arch.hvm_vcpu.hvm_trace_values[2],
+ v->arch.hvm_vcpu.hvm_trace_values[3],
+ v->arch.hvm_vcpu.hvm_trace_values[4]);
TRACE_VMEXIT(0, 0);
TRACE_VMEXIT(1, 0);
diff --git a/xen/arch/x86/hvm/vmx/x86_32/exits.S b/xen/arch/x86/hvm/vmx/x86_32/exits.S
index d0ef963b86..144dd174d4 100644
--- a/xen/arch/x86/hvm/vmx/x86_32/exits.S
+++ b/xen/arch/x86/hvm/vmx/x86_32/exits.S
@@ -105,7 +105,8 @@ ENTRY(vmx_asm_do_vmentry)
jnz vmx_process_softirqs
call vmx_intr_assist
- call vmx_load_cr2
+ movl VCPU_vmx_cr2(%ebx),%eax
+ movl %eax,%cr2
call vmx_trace_vmentry
cmpl $0,VCPU_vmx_launched(%ebx)
diff --git a/xen/arch/x86/hvm/vmx/x86_64/exits.S b/xen/arch/x86/hvm/vmx/x86_64/exits.S
index 275e8938b2..d427ac2cd5 100644
--- a/xen/arch/x86/hvm/vmx/x86_64/exits.S
+++ b/xen/arch/x86/hvm/vmx/x86_64/exits.S
@@ -115,7 +115,8 @@ ENTRY(vmx_asm_do_vmentry)
jnz vmx_process_softirqs
call vmx_intr_assist
- call vmx_load_cr2
+ movq VCPU_vmx_cr2(%rbx),%rax
+ movq %rax,%cr2
call vmx_trace_vmentry
cmpl $0,VCPU_vmx_launched(%rbx)
diff --git a/xen/arch/x86/io_apic.c b/xen/arch/x86/io_apic.c
index 4b30851276..d56351e7aa 100644
--- a/xen/arch/x86/io_apic.c
+++ b/xen/arch/x86/io_apic.c
@@ -1980,7 +1980,8 @@ int ioapic_guest_read(unsigned long physbase, unsigned int reg, u32 *pval)
}
#define WARN_BOGUS_WRITE(f, a...) \
- DPRINTK("\n%s: apic=%d, pin=%d, old_irq=%d, new_irq=%d\n" \
+ dprintk(XENLOG_INFO, "\n%s: " \
+ "apic=%d, pin=%d, old_irq=%d, new_irq=%d\n" \
"%s: old_entry=%08x, new_entry=%08x\n" \
"%s: " f, __FUNCTION__, apic, pin, old_irq, new_irq, \
__FUNCTION__, *(u32 *)&old_rte, *(u32 *)&new_rte, \
diff --git a/xen/arch/x86/irq.c b/xen/arch/x86/irq.c
index 4bad3ee23e..1821a56e24 100644
--- a/xen/arch/x86/irq.c
+++ b/xen/arch/x86/irq.c
@@ -432,7 +432,8 @@ int pirq_guest_bind(struct vcpu *v, int irq, int will_share)
{
if ( desc->action != NULL )
{
- DPRINTK("Cannot bind IRQ %d to guest. In use by '%s'.\n",
+ gdprintk(XENLOG_INFO,
+ "Cannot bind IRQ %d to guest. In use by '%s'.\n",
irq, desc->action->name);
rc = -EBUSY;
goto out;
@@ -441,7 +442,9 @@ int pirq_guest_bind(struct vcpu *v, int irq, int will_share)
action = xmalloc(irq_guest_action_t);
if ( (desc->action = (struct irqaction *)action) == NULL )
{
- DPRINTK("Cannot bind IRQ %d to guest. Out of memory.\n", irq);
+ gdprintk(XENLOG_INFO,
+ "Cannot bind IRQ %d to guest. Out of memory.\n",
+ irq);
rc = -ENOMEM;
goto out;
}
@@ -464,7 +467,8 @@ int pirq_guest_bind(struct vcpu *v, int irq, int will_share)
}
else if ( !will_share || !action->shareable )
{
- DPRINTK("Cannot bind IRQ %d to guest. Will not share with others.\n",
+ gdprintk(XENLOG_INFO, "Cannot bind IRQ %d to guest. "
+ "Will not share with others.\n",
irq);
rc = -EBUSY;
goto out;
@@ -484,7 +488,8 @@ int pirq_guest_bind(struct vcpu *v, int irq, int will_share)
if ( action->nr_guests == IRQ_MAX_GUESTS )
{
- DPRINTK("Cannot bind IRQ %d to guest. Already at max share.\n", irq);
+ gdprintk(XENLOG_INFO, "Cannot bind IRQ %d to guest. "
+ "Already at max share.\n", irq);
rc = -EBUSY;
goto out;
}
diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c
index f5098d9038..d3b4d0287e 100644
--- a/xen/arch/x86/mm.c
+++ b/xen/arch/x86/mm.c
@@ -108,13 +108,7 @@
#include <asm/e820.h>
#include <public/memory.h>
-#ifdef VERBOSE
-#define MEM_LOG(_f, _a...) \
- printk("DOM%u: (file=mm.c, line=%d) " _f "\n", \
- current->domain->domain_id , __LINE__ , ## _a )
-#else
-#define MEM_LOG(_f, _a...) ((void)0)
-#endif
+#define MEM_LOG(_f, _a...) gdprintk(XENLOG_WARNING , _f "\n" , ## _a)
/*
* PTE updates can be done with ordinary writes except:
@@ -578,8 +572,9 @@ get_page_from_l1e(
if ( !iomem_access_permitted(d, mfn, mfn) )
{
- MEM_LOG("Non-privileged (%u) attempt to map I/O space %08lx",
- d->domain_id, mfn);
+ if ( mfn != (PADDR_MASK >> PAGE_SHIFT) ) /* INVALID_MFN? */
+ MEM_LOG("Non-privileged (%u) attempt to map I/O space %08lx",
+ d->domain_id, mfn);
return 0;
}
@@ -1721,7 +1716,7 @@ int new_guest_cr3(unsigned long mfn)
int okay;
unsigned long old_base_mfn;
- if ( hvm_guest(v) && !hvm_paging_enabled(v) )
+ if ( is_hvm_domain(d) && !hvm_paging_enabled(v) )
domain_crash_synchronous();
if ( shadow_mode_refcounts(d) )
@@ -2258,7 +2253,7 @@ int do_mmu_update(
{
if ( shadow_mode_refcounts(d) )
{
- DPRINTK("mmu update on shadow-refcounted domain!");
+ MEM_LOG("mmu update on shadow-refcounted domain!");
break;
}
@@ -2385,7 +2380,7 @@ int do_mmu_update(
static int create_grant_pte_mapping(
- unsigned long pte_addr, l1_pgentry_t nl1e, struct vcpu *v)
+ uint64_t pte_addr, l1_pgentry_t nl1e, struct vcpu *v)
{
int rc = GNTST_okay;
void *va;
@@ -2409,7 +2404,7 @@ static int create_grant_pte_mapping(
}
va = map_domain_page(mfn);
- va = (void *)((unsigned long)va + (pte_addr & ~PAGE_MASK));
+ va = (void *)((unsigned long)va + ((unsigned long)pte_addr & ~PAGE_MASK));
page = mfn_to_page(mfn);
type = page->u.inuse.type_info & PGT_type_mask;
@@ -2441,7 +2436,7 @@ static int create_grant_pte_mapping(
}
static int destroy_grant_pte_mapping(
- unsigned long addr, unsigned long frame, struct domain *d)
+ uint64_t addr, unsigned long frame, struct domain *d)
{
int rc = GNTST_okay;
void *va;
@@ -2460,7 +2455,7 @@ static int destroy_grant_pte_mapping(
}
va = map_domain_page(mfn);
- va = (void *)((unsigned long)va + (addr & ~PAGE_MASK));
+ va = (void *)((unsigned long)va + ((unsigned long)addr & ~PAGE_MASK));
page = mfn_to_page(mfn);
type = page->u.inuse.type_info & PGT_type_mask;
@@ -2481,7 +2476,7 @@ static int destroy_grant_pte_mapping(
/* Check that the virtual address supplied is actually mapped to frame. */
if ( unlikely((l1e_get_intpte(ol1e) >> PAGE_SHIFT) != frame) )
{
- MEM_LOG("PTE entry %lx for address %lx doesn't match frame %lx",
+ MEM_LOG("PTE entry %lx for address %"PRIx64" doesn't match frame %lx",
(unsigned long)l1e_get_intpte(ol1e), addr, frame);
put_page_type(page);
rc = GNTST_general_error;
@@ -2581,7 +2576,7 @@ static int destroy_grant_va_mapping(
}
int create_grant_host_mapping(
- unsigned long addr, unsigned long frame, unsigned int flags)
+ uint64_t addr, unsigned long frame, unsigned int flags)
{
l1_pgentry_t pte = l1e_from_pfn(frame, GRANT_PTE_FLAGS);
@@ -2596,7 +2591,7 @@ int create_grant_host_mapping(
}
int destroy_grant_host_mapping(
- unsigned long addr, unsigned long frame, unsigned int flags)
+ uint64_t addr, unsigned long frame, unsigned int flags)
{
if ( flags & GNTMAP_contains_pte )
return destroy_grant_pte_mapping(addr, frame, current->domain);
@@ -2622,7 +2617,7 @@ int steal_page(
x = y;
if (unlikely((x & (PGC_count_mask|PGC_allocated)) !=
(1 | PGC_allocated)) || unlikely(_nd != _d)) {
- DPRINTK("gnttab_transfer: Bad page %p: ed=%p(%u), sd=%p,"
+ MEM_LOG("gnttab_transfer: Bad page %p: ed=%p(%u), sd=%p,"
" caf=%08x, taf=%" PRtype_info "\n",
(void *) page_to_mfn(page),
d, d->domain_id, unpickle_domptr(_nd), x,
diff --git a/xen/arch/x86/mm/shadow/common.c b/xen/arch/x86/mm/shadow/common.c
index c053e64ffb..373fa491e1 100644
--- a/xen/arch/x86/mm/shadow/common.c
+++ b/xen/arch/x86/mm/shadow/common.c
@@ -578,6 +578,7 @@ void shadow_prealloc(struct domain *d, unsigned int order)
v = current;
if ( v->domain != d )
v = d->vcpu[0];
+ ASSERT(v != NULL);
/* Stage one: walk the list of top-level pages, unpinning them */
perfc_incrc(shadow_prealloc_1);
@@ -634,6 +635,56 @@ void shadow_prealloc(struct domain *d, unsigned int order)
BUG();
}
+#ifndef NDEBUG
+/* Deliberately free all the memory we can: this can be used to cause the
+ * guest's pagetables to be re-shadowed if we suspect that the shadows
+ * have somehow got out of sync */
+static void shadow_blow_tables(unsigned char c)
+{
+ struct list_head *l, *t;
+ struct page_info *pg;
+ struct domain *d;
+ struct vcpu *v;
+ mfn_t smfn;
+
+ for_each_domain(d)
+ {
+ if ( shadow_mode_enabled(d) && (v = d->vcpu[0]) != NULL)
+ {
+ shadow_lock(d);
+ printk("Blowing shadow tables for domain %u\n", d->domain_id);
+
+ /* Pass one: unpin all top-level pages */
+ list_for_each_backwards_safe(l,t, &d->arch.shadow.toplevel_shadows)
+ {
+ pg = list_entry(l, struct page_info, list);
+ smfn = page_to_mfn(pg);
+ sh_unpin(v, smfn);
+ }
+
+ /* Second pass: unhook entries of in-use shadows */
+ list_for_each_backwards_safe(l,t, &d->arch.shadow.toplevel_shadows)
+ {
+ pg = list_entry(l, struct page_info, list);
+ smfn = page_to_mfn(pg);
+ shadow_unhook_mappings(v, smfn);
+ }
+
+ /* Make sure everyone sees the unshadowings */
+ flush_tlb_mask(d->domain_dirty_cpumask);
+ shadow_unlock(d);
+ }
+ }
+}
+
+/* Register this function in the Xen console keypress table */
+static __init int shadow_blow_tables_keyhandler_init(void)
+{
+ register_keyhandler('S', shadow_blow_tables, "reset shadow pagetables");
+ return 0;
+}
+__initcall(shadow_blow_tables_keyhandler_init);
+#endif /* !NDEBUG */
/* Allocate another shadow's worth of (contiguous, aligned) pages,
* and fill in the type and backpointer fields of their page_infos.
@@ -941,9 +992,9 @@ p2m_next_level(struct domain *d, mfn_t *table_mfn, void **table,
}
#endif
/* The P2M can be shadowed: keep the shadows synced */
- if ( d->vcpu[0] )
+ if ( d->vcpu[0] != NULL )
(void)__shadow_validate_guest_entry(d->vcpu[0], *table_mfn,
- p2m_entry, sizeof *p2m_entry);
+ p2m_entry, sizeof *p2m_entry);
}
*table_mfn = _mfn(l1e_get_pfn(*p2m_entry));
next = sh_map_domain_page(*table_mfn);
@@ -997,8 +1048,9 @@ shadow_set_p2m_entry(struct domain *d, unsigned long gfn, mfn_t mfn)
*p2m_entry = l1e_empty();
/* The P2M can be shadowed: keep the shadows synced */
- (void) __shadow_validate_guest_entry(d->vcpu[0], table_mfn,
- p2m_entry, sizeof *p2m_entry);
+ if ( d->vcpu[0] != NULL )
+ (void)__shadow_validate_guest_entry(
+ d->vcpu[0], table_mfn, p2m_entry, sizeof(*p2m_entry));
sh_unmap_domain_page(table);
@@ -1015,9 +1067,11 @@ shadow_set_p2m_entry(struct domain *d, unsigned long gfn, mfn_t mfn)
static int
shadow_alloc_p2m_table(struct domain *d)
{
- mfn_t p2m_top;
+ mfn_t p2m_top, mfn;
struct list_head *entry;
+ struct page_info *page;
unsigned int page_count = 0;
+ unsigned long gfn;
SHADOW_PRINTK("allocating p2m table\n");
ASSERT(pagetable_get_pfn(d->arch.phys_table) == 0);
@@ -1041,13 +1095,19 @@ shadow_alloc_p2m_table(struct domain *d)
SHADOW_PRINTK("populating p2m table\n");
+ /* Initialise physmap tables for slot zero. Other code assumes this. */
+ gfn = 0;
+ mfn = _mfn(INVALID_MFN);
+ if ( !shadow_set_p2m_entry(d, gfn, mfn) )
+ goto error;
+
for ( entry = d->page_list.next;
entry != &d->page_list;
entry = entry->next )
{
- struct page_info *page = list_entry(entry, struct page_info, list);
- mfn_t mfn = page_to_mfn(page);
- unsigned long gfn = get_gpfn_from_mfn(mfn_x(mfn));
+ page = list_entry(entry, struct page_info, list);
+ mfn = page_to_mfn(page);
+ gfn = get_gpfn_from_mfn(mfn_x(mfn));
page_count++;
if (
#ifdef __x86_64__
@@ -1057,15 +1117,16 @@ shadow_alloc_p2m_table(struct domain *d)
#endif
&& gfn != INVALID_M2P_ENTRY
&& !shadow_set_p2m_entry(d, gfn, mfn) )
- {
- SHADOW_PRINTK("failed to initialize p2m table, gfn=%05lx, mfn=%" SH_PRI_mfn "\n",
- gfn, mfn_x(mfn));
- return 0;
- }
+ goto error;
}
SHADOW_PRINTK("p2m table initialised (%u pages)\n", page_count);
return 1;
+
+ error:
+ SHADOW_PRINTK("failed to initialize p2m table, gfn=%05lx, mfn=%"
+ SH_PRI_mfn "\n", gfn, mfn_x(mfn));
+ return 0;
}
mfn_t
@@ -1327,8 +1388,18 @@ static void sh_hash_audit_bucket(struct domain *d, int bucket)
&& e->t != (PGC_SH_fl1_pae_shadow >> PGC_SH_type_shift)
&& e->t != (PGC_SH_fl1_64_shadow >> PGC_SH_type_shift) )
{
+ struct page_info *gpg = mfn_to_page(_mfn(e->n));
/* Bad shadow flags on guest page? */
- BUG_ON( !(mfn_to_page(_mfn(e->n))->shadow_flags & (1<<e->t)) );
+ BUG_ON( !(gpg->shadow_flags & (1<<e->t)) );
+ /* Bad type count on guest page? */
+ if ( (gpg->u.inuse.type_info & PGT_type_mask) == PGT_writable_page
+ && (gpg->u.inuse.type_info & PGT_count_mask) != 0 )
+ {
+ SHADOW_ERROR("MFN %#"SH_PRI_mfn" shadowed (by %#"SH_PRI_mfn")"
+ " but has typecount %#lx\n",
+ e->n, mfn_x(e->smfn), gpg->u.inuse.type_info);
+ BUG();
+ }
}
/* That entry was OK; on we go */
e = e->next;
@@ -2263,20 +2334,11 @@ void sh_update_paging_modes(struct vcpu *v)
// - changes in CR0.PG, CR4.PAE, CR4.PSE, or CR4.PGE
//
- // Avoid determining the current shadow mode for uninitialized CPUs, as
- // we can not yet determine whether it is an HVM or PV domain.
- //
- if ( !test_bit(_VCPUF_initialised, &v->vcpu_flags) )
- {
- SHADOW_PRINTK("%s: postponing determination of shadow mode\n", __func__);
- return;
- }
-
// First, tear down any old shadow tables held by this vcpu.
//
shadow_detach_old_tables(v);
- if ( !hvm_guest(v) )
+ if ( !is_hvm_domain(d) )
{
///
/// PV guest
@@ -2306,7 +2368,6 @@ void sh_update_paging_modes(struct vcpu *v)
v->arch.shadow.translate_enabled = !!hvm_paging_enabled(v);
if ( !v->arch.shadow.translate_enabled )
{
-
/* Set v->arch.guest_table to use the p2m map, and choose
* the appropriate shadow mode */
old_guest_table = pagetable_get_mfn(v->arch.guest_table);
@@ -2384,7 +2445,7 @@ void sh_update_paging_modes(struct vcpu *v)
SHADOW_PRINTK("new paging mode: d=%u v=%u pe=%d g=%u s=%u "
"(was g=%u s=%u)\n",
d->domain_id, v->vcpu_id,
- hvm_guest(v) ? !!hvm_paging_enabled(v) : 1,
+ is_hvm_domain(d) ? !!hvm_paging_enabled(v) : 1,
v->arch.shadow.mode->guest_levels,
v->arch.shadow.mode->shadow_levels,
old_mode ? old_mode->guest_levels : 0,
@@ -2451,7 +2512,7 @@ static void sh_new_mode(struct domain *d, u32 new_mode)
sh_update_paging_modes(v);
}
-static int shadow_enable(struct domain *d, u32 mode)
+int shadow_enable(struct domain *d, u32 mode)
/* Turn on "permanent" shadow features: external, translate, refcount.
* Can only be called once on a domain, and these features cannot be
* disabled.
@@ -2837,15 +2898,18 @@ sh_p2m_remove_page(struct domain *d, unsigned long gfn, unsigned long mfn)
if ( v->domain != d )
v = d->vcpu[0];
-
SHADOW_DEBUG(P2M, "removing gfn=%#lx mfn=%#lx\n", gfn, mfn);
ASSERT(mfn_x(sh_gfn_to_mfn(d, gfn)) == mfn);
//ASSERT(sh_mfn_to_gfn(d, mfn) == gfn);
- shadow_remove_all_shadows_and_parents(v, _mfn(mfn));
- if ( shadow_remove_all_mappings(v, _mfn(mfn)) )
- flush_tlb_mask(d->domain_dirty_cpumask);
+ if ( v != NULL )
+ {
+ shadow_remove_all_shadows_and_parents(v, _mfn(mfn));
+ if ( shadow_remove_all_mappings(v, _mfn(mfn)) )
+ flush_tlb_mask(d->domain_dirty_cpumask);
+ }
+
shadow_set_p2m_entry(d, gfn, _mfn(INVALID_MFN));
set_gpfn_from_mfn(mfn, INVALID_M2P_ENTRY);
}
@@ -2865,17 +2929,12 @@ void
shadow_guest_physmap_add_page(struct domain *d, unsigned long gfn,
unsigned long mfn)
{
- struct vcpu *v;
unsigned long ogfn;
mfn_t omfn;
if ( !shadow_mode_translate(d) )
return;
- v = current;
- if ( v->domain != d )
- v = d->vcpu[0];
-
shadow_lock(d);
shadow_audit_p2m(d);
@@ -2885,11 +2944,17 @@ shadow_guest_physmap_add_page(struct domain *d, unsigned long gfn,
if ( valid_mfn(omfn) )
{
/* Get rid of the old mapping, especially any shadows */
- shadow_remove_all_shadows_and_parents(v, omfn);
- if ( shadow_remove_all_mappings(v, omfn) )
- flush_tlb_mask(d->domain_dirty_cpumask);
+ struct vcpu *v = current;
+ if ( v->domain != d )
+ v = d->vcpu[0];
+ if ( v != NULL )
+ {
+ shadow_remove_all_shadows_and_parents(v, omfn);
+ if ( shadow_remove_all_mappings(v, omfn) )
+ flush_tlb_mask(d->domain_dirty_cpumask);
+ }
set_gpfn_from_mfn(mfn_x(omfn), INVALID_M2P_ENTRY);
- }
+ }
ogfn = sh_mfn_to_gfn(d, _mfn(mfn));
if (
@@ -2961,7 +3026,8 @@ static int shadow_log_dirty_op(
list_for_each_safe(l, t, &d->arch.shadow.toplevel_shadows)
{
pg = list_entry(l, struct page_info, list);
- shadow_unhook_mappings(d->vcpu[0], page_to_mfn(pg));
+ if ( d->vcpu[0] != NULL )
+ shadow_unhook_mappings(d->vcpu[0], page_to_mfn(pg));
}
d->arch.shadow.fault_count = 0;
@@ -3072,7 +3138,7 @@ int shadow_domctl(struct domain *d,
if ( unlikely(d == current->domain) )
{
- DPRINTK("Don't try to do a shadow op on yourself!\n");
+ gdprintk(XENLOG_INFO, "Don't try to do a shadow op on yourself!\n");
return -EINVAL;
}
@@ -3082,6 +3148,8 @@ int shadow_domctl(struct domain *d,
if ( shadow_mode_log_dirty(d) )
if ( (rc = shadow_log_dirty_disable(d)) != 0 )
return rc;
+ if ( is_hvm_domain(d) )
+ return -EINVAL;
if ( d->arch.shadow.mode & SHM2_enable )
if ( (rc = shadow_test_disable(d)) != 0 )
return rc;
diff --git a/xen/arch/x86/mm/shadow/multi.c b/xen/arch/x86/mm/shadow/multi.c
index b70c979fbd..e2e0bb7a3b 100644
--- a/xen/arch/x86/mm/shadow/multi.c
+++ b/xen/arch/x86/mm/shadow/multi.c
@@ -36,14 +36,7 @@
#include "private.h"
#include "types.h"
-/* The first cut: an absolutely synchronous, trap-and-emulate version,
- * supporting only HVM guests (and so only "external" shadow mode).
- *
- * THINGS TO DO LATER:
- *
- * FIX GVA_TO_GPA
- * The current interface returns an unsigned long, which is not big enough
- * to hold a physical address in PAE. Should return a gfn instead.
+/* THINGS TO DO LATER:
*
* TEARDOWN HEURISTICS
* Also: have a heuristic for when to destroy a previous paging-mode's
@@ -60,14 +53,6 @@
* l3-and-l2h-only shadow mode for PAE PV guests that would allow them
* to share l2h pages again.
*
- * PAE L3 COPYING
- * In this code, we copy all 32 bytes of a PAE L3 every time we change an
- * entry in it, and every time we change CR3. We copy it for the linear
- * mappings (ugh! PAE linear mappings) and we copy it to the low-memory
- * buffer so it fits in CR3. Maybe we can avoid some of this recopying
- * by using the shadow directly in some places.
- * Also, for SMP, need to actually respond to seeing shadow.pae_flip_pending.
- *
* GUEST_WALK_TABLES TLB FLUSH COALESCE
* guest_walk_tables can do up to three remote TLB flushes as it walks to
* the first l1 of a new pagetable. Should coalesce the flushes to the end,
@@ -103,9 +88,6 @@ static char *fetch_type_names[] = {
};
#endif
-/* XXX forward declarations */
-static inline void sh_update_linear_entries(struct vcpu *v);
-
/**************************************************************************/
/* Hash table mapping from guest pagetables to shadows
*
@@ -220,14 +202,14 @@ guest_supports_superpages(struct vcpu *v)
{
/* The _PAGE_PSE bit must be honoured in HVM guests, whenever
* CR4.PSE is set or the guest is in PAE or long mode */
- return (hvm_guest(v) && (GUEST_PAGING_LEVELS != 2
+ return (is_hvm_vcpu(v) && (GUEST_PAGING_LEVELS != 2
|| (hvm_get_guest_ctrl_reg(v, 4) & X86_CR4_PSE)));
}
static inline int
guest_supports_nx(struct vcpu *v)
{
- if ( !hvm_guest(v) )
+ if ( !is_hvm_vcpu(v) )
return cpu_has_nx;
// XXX - fix this!
@@ -464,17 +446,21 @@ static u32 guest_set_ad_bits(struct vcpu *v,
u32 flags;
int res = 0;
- ASSERT(valid_mfn(gmfn)
- && (sh_mfn_is_a_page_table(gmfn)
- || ((mfn_to_page(gmfn)->u.inuse.type_info & PGT_count_mask)
- == 0)));
ASSERT(ep && !(((unsigned long)ep) & ((sizeof *ep) - 1)));
ASSERT(level <= GUEST_PAGING_LEVELS);
- ASSERT(ft == ft_demand_read || ft == ft_demand_write);
ASSERT(shadow_lock_is_acquired(v->domain));
flags = guest_l1e_get_flags(*ep);
+ /* Only set A and D bits for guest-initiated accesses */
+ if ( !(ft & FETCH_TYPE_DEMAND) )
+ return flags;
+
+ ASSERT(valid_mfn(gmfn)
+ && (sh_mfn_is_a_page_table(gmfn)
+ || ((mfn_to_page(gmfn)->u.inuse.type_info & PGT_count_mask)
+ == 0)));
+
/* PAE l3s do not have A and D bits */
ASSERT(GUEST_PAGING_LEVELS > 3 || level != 3);
@@ -500,12 +486,20 @@ static u32 guest_set_ad_bits(struct vcpu *v,
/* Set the bit(s) */
sh_mark_dirty(v->domain, gmfn);
SHADOW_DEBUG(A_AND_D, "gfn = %" SH_PRI_gfn ", "
- "old flags = %#x, new flags = %#x\n",
- gfn_x(guest_l1e_get_gfn(*ep)), guest_l1e_get_flags(*ep), flags);
+ "old flags = %#x, new flags = %#x\n",
+ gfn_x(guest_l1e_get_gfn(*ep)), guest_l1e_get_flags(*ep),
+ flags);
*ep = guest_l1e_from_gfn(guest_l1e_get_gfn(*ep), flags);
- /* Propagate this change to any existing shadows */
- res = __shadow_validate_guest_entry(v, gmfn, ep, sizeof(*ep));
+ /* Propagate this change to any other shadows of the page
+ * (only necessary if there is more than one shadow) */
+ if ( mfn_to_page(gmfn)->count_info & PGC_page_table )
+ {
+ u32 shflags = mfn_to_page(gmfn)->shadow_flags & SHF_page_type_mask;
+ /* More than one type bit set in shadow-flags? */
+ if ( shflags & ~(1UL << find_first_set_bit(shflags)) )
+ res = __shadow_validate_guest_entry(v, gmfn, ep, sizeof(*ep));
+ }
/* We should never need to flush the TLB or recopy PAE entries */
ASSERT((res == 0) || (res == SHADOW_SET_CHANGED));
@@ -641,79 +635,70 @@ shadow_l4_index(mfn_t *smfn, u32 guest_index)
/**************************************************************************/
-/* Functions which compute shadow entries from their corresponding guest
- * entries.
- *
- * These are the "heart" of the shadow code.
- *
- * There are two sets of these: those that are called on demand faults (read
- * faults and write faults), and those that are essentially called to
- * "prefetch" (or propagate) entries from the guest into the shadow. The read
- * fault and write fault are handled as two separate cases for L1 entries (due
- * to the _PAGE_DIRTY bit handling), but for L[234], they are grouped together
- * into the respective demand_fault functions.
+/* Function which computes shadow entries from their corresponding guest
+ * entries. This is the "heart" of the shadow code. It operates using
+ * level-1 shadow types, but handles all levels of entry.
+ * Don't call it directly, but use the four wrappers below.
*/
-// The function below tries to capture all of the flag manipulation for the
-// demand and propagate functions into one place.
-//
-static always_inline u32
-sh_propagate_flags(struct vcpu *v, mfn_t target_mfn,
- u32 gflags, guest_l1e_t *guest_entry_ptr, mfn_t gmfn,
- int mmio, int level, fetch_type_t ft)
-{
-#define CHECK(_cond) \
-do { \
- if (unlikely(!(_cond))) \
- { \
- printk("%s %s %d ASSERTION (%s) FAILED\n", \
- __func__, __FILE__, __LINE__, #_cond); \
- domain_crash(d); \
- } \
-} while (0);
+static always_inline void
+_sh_propagate(struct vcpu *v,
+ void *guest_entry_ptr,
+ mfn_t guest_table_mfn,
+ mfn_t target_mfn,
+ void *shadow_entry_ptr,
+ int level,
+ fetch_type_t ft,
+ int mmio)
+{
+ guest_l1e_t *gp = guest_entry_ptr;
+ shadow_l1e_t *sp = shadow_entry_ptr;
struct domain *d = v->domain;
u32 pass_thru_flags;
- u32 sflags;
+ u32 gflags, sflags;
/* We don't shadow PAE l3s */
ASSERT(GUEST_PAGING_LEVELS > 3 || level != 3);
- // XXX -- might want to think about PAT support for HVM guests...
-
-#ifndef NDEBUG
- // MMIO can only occur from L1e's
- //
- if ( mmio )
- CHECK(level == 1);
-
- // We should always have a pointer to the guest entry if it's a non-PSE
- // non-MMIO demand access.
- if ( ft & FETCH_TYPE_DEMAND )
- CHECK(guest_entry_ptr || level == 1);
-#endif
+ if ( valid_mfn(guest_table_mfn) )
+ /* Handle A and D bit propagation into the guest */
+ gflags = guest_set_ad_bits(v, guest_table_mfn, gp, level, ft);
+ else
+ {
+ /* Must be an fl1e or a prefetch */
+ ASSERT(level==1 || !(ft & FETCH_TYPE_DEMAND));
+ gflags = guest_l1e_get_flags(*gp);
+ }
- // A not-present guest entry has a special signature in the shadow table,
- // so that we do not have to consult the guest tables multiple times...
- //
if ( unlikely(!(gflags & _PAGE_PRESENT)) )
- return _PAGE_SHADOW_GUEST_NOT_PRESENT;
+ {
+ /* If a guest l1 entry is not present, shadow with the magic
+ * guest-not-present entry. */
+ if ( level == 1 )
+ *sp = sh_l1e_gnp();
+ else
+ *sp = shadow_l1e_empty();
+ goto done;
+ }
+
+ if ( level == 1 && mmio )
+ {
+ /* Guest l1e maps MMIO space */
+ *sp = sh_l1e_mmio(guest_l1e_get_gfn(*gp), gflags);
+ goto done;
+ }
- // Must have a valid target_mfn, unless this is mmio, or unless this is a
- // prefetch. In the case of a prefetch, an invalid mfn means that we can
- // not usefully shadow anything, and so we return early.
+ // Must have a valid target_mfn, unless this is a prefetch. In the
+ // case of a prefetch, an invalid mfn means that we can not usefully
+ // shadow anything, and so we return early.
//
if ( !valid_mfn(target_mfn) )
{
- CHECK((ft == ft_prefetch) || mmio);
- if ( !mmio )
- return 0;
+ ASSERT((ft == ft_prefetch));
+ *sp = shadow_l1e_empty();
+ goto done;
}
- // Set the A and D bits in the guest entry, if we need to.
- if ( guest_entry_ptr && (ft & FETCH_TYPE_DEMAND) )
- gflags = guest_set_ad_bits(v, gmfn, guest_entry_ptr, level, ft);
-
-
// Propagate bits from the guest to the shadow.
// Some of these may be overwritten, below.
// Since we know the guest's PRESENT bit is set, we also set the shadow's
@@ -723,12 +708,7 @@ do { \
_PAGE_RW | _PAGE_PRESENT);
if ( guest_supports_nx(v) )
pass_thru_flags |= _PAGE_NX_BIT;
- sflags = (gflags & pass_thru_flags) | _PAGE_SHADOW_PRESENT;
-
- // Copy the guest's RW bit into the SHADOW_RW bit.
- //
- if ( gflags & _PAGE_RW )
- sflags |= _PAGE_SHADOW_RW;
+ sflags = gflags & pass_thru_flags;
// Set the A&D bits for higher level shadows.
// Higher level entries do not, strictly speaking, have dirty bits, but
@@ -754,83 +734,68 @@ do { \
&& !(gflags & _PAGE_DIRTY)) )
sflags &= ~_PAGE_RW;
- // MMIO caching
+ // shadow_mode_log_dirty support
//
- // MMIO mappings are marked as not present, but we set the SHADOW_MMIO bit
- // to cache the fact that this entry is in MMIO space.
+ // Only allow the guest write access to a page a) on a demand fault,
+ // or b) if the page is already marked as dirty.
//
- if ( (level == 1) && mmio )
+ if ( unlikely((level == 1) && shadow_mode_log_dirty(d)) )
{
- sflags &= ~(_PAGE_PRESENT);
- sflags |= _PAGE_SHADOW_MMIO;
+ if ( ft & FETCH_TYPE_WRITE )
+ sh_mark_dirty(d, target_mfn);
+ else if ( !sh_mfn_is_dirty(d, target_mfn) )
+ sflags &= ~_PAGE_RW;
}
- else
+
+ // protect guest page tables
+ //
+ if ( unlikely((level == 1) && sh_mfn_is_a_page_table(target_mfn)) )
{
- // shadow_mode_log_dirty support
- //
- // Only allow the guest write access to a page a) on a demand fault,
- // or b) if the page is already marked as dirty.
- //
- if ( unlikely((level == 1) &&
- !(ft & FETCH_TYPE_WRITE) &&
- shadow_mode_log_dirty(d) &&
- !sh_mfn_is_dirty(d, target_mfn)) )
+ if ( shadow_mode_trap_reads(d) )
{
- sflags &= ~_PAGE_RW;
+ // if we are trapping both reads & writes, then mark this page
+ // as not present...
+ //
+ sflags &= ~_PAGE_PRESENT;
}
-
- // protect guest page tables
- //
- if ( unlikely((level == 1) &&
- sh_mfn_is_a_page_table(target_mfn)) )
+ else
{
- if ( shadow_mode_trap_reads(d) )
- {
- // if we are trapping both reads & writes, then mark this page
- // as not present...
- //
- sflags &= ~_PAGE_PRESENT;
- }
- else
- {
- // otherwise, just prevent any writes...
- //
- sflags &= ~_PAGE_RW;
- }
+ // otherwise, just prevent any writes...
+ //
+ sflags &= ~_PAGE_RW;
}
}
// PV guests in 64-bit mode use two different page tables for user vs
// supervisor permissions, making the guest's _PAGE_USER bit irrelevant.
// It is always shadowed as present...
- if ( (GUEST_PAGING_LEVELS == 4) && !hvm_guest(v) )
+ if ( (GUEST_PAGING_LEVELS == 4) && !is_hvm_domain(d) )
{
sflags |= _PAGE_USER;
}
- return sflags;
-#undef CHECK
+ *sp = shadow_l1e_from_mfn(target_mfn, sflags);
+ done:
+ SHADOW_DEBUG(PROPAGATE,
+ "%s level %u guest %" SH_PRI_gpte " shadow %" SH_PRI_pte "\n",
+ fetch_type_names[ft], level, gp->l1, sp->l1);
}
+
+/* These four wrappers give us a little bit of type-safety back around the
+ * use of void-* pointers in _sh_propagate(), and allow the compiler to
+ * optimize out some level checks. */
+
#if GUEST_PAGING_LEVELS >= 4
static void
l4e_propagate_from_guest(struct vcpu *v,
guest_l4e_t *gl4e,
mfn_t gl4mfn,
mfn_t sl3mfn,
- shadow_l4e_t *sl4p,
+ shadow_l4e_t *sl4e,
fetch_type_t ft)
{
- u32 gflags = guest_l4e_get_flags(*gl4e);
- u32 sflags = sh_propagate_flags(v, sl3mfn, gflags, (guest_l1e_t *) gl4e,
- gl4mfn, 0, 4, ft);
-
- *sl4p = shadow_l4e_from_mfn(sl3mfn, sflags);
-
- SHADOW_DEBUG(PROPAGATE,
- "%s gl4e=%" SH_PRI_gpte " sl4e=%" SH_PRI_pte "\n",
- fetch_type_names[ft], gl4e->l4, sl4p->l4);
- ASSERT(sflags != -1);
+ _sh_propagate(v, gl4e, gl4mfn, sl3mfn, sl4e, 4, ft, 0);
}
static void
@@ -838,19 +803,10 @@ l3e_propagate_from_guest(struct vcpu *v,
guest_l3e_t *gl3e,
mfn_t gl3mfn,
mfn_t sl2mfn,
- shadow_l3e_t *sl3p,
+ shadow_l3e_t *sl3e,
fetch_type_t ft)
{
- u32 gflags = guest_l3e_get_flags(*gl3e);
- u32 sflags = sh_propagate_flags(v, sl2mfn, gflags, (guest_l1e_t *) gl3e,
- gl3mfn, 0, 3, ft);
-
- *sl3p = shadow_l3e_from_mfn(sl2mfn, sflags);
-
- SHADOW_DEBUG(PROPAGATE,
- "%s gl3e=%" SH_PRI_gpte " sl3e=%" SH_PRI_pte "\n",
- fetch_type_names[ft], gl3e->l3, sl3p->l3);
- ASSERT(sflags != -1);
+ _sh_propagate(v, gl3e, gl3mfn, sl2mfn, sl3e, 3, ft, 0);
}
#endif // GUEST_PAGING_LEVELS >= 4
@@ -858,95 +814,23 @@ static void
l2e_propagate_from_guest(struct vcpu *v,
guest_l2e_t *gl2e,
mfn_t gl2mfn,
- mfn_t sl1mfn,
- shadow_l2e_t *sl2p,
+ mfn_t sl1mfn,
+ shadow_l2e_t *sl2e,
fetch_type_t ft)
{
- u32 gflags = guest_l2e_get_flags(*gl2e);
- u32 sflags = sh_propagate_flags(v, sl1mfn, gflags, (guest_l1e_t *) gl2e,
- gl2mfn, 0, 2, ft);
-
- *sl2p = shadow_l2e_from_mfn(sl1mfn, sflags);
-
- SHADOW_DEBUG(PROPAGATE,
- "%s gl2e=%" SH_PRI_gpte " sl2e=%" SH_PRI_pte "\n",
- fetch_type_names[ft], gl2e->l2, sl2p->l2);
- ASSERT(sflags != -1);
-}
-
-static inline int
-l1e_read_fault(struct vcpu *v, walk_t *gw, mfn_t gmfn, shadow_l1e_t *sl1p,
- int mmio)
-/* returns 1 if emulation is required, and 0 otherwise */
-{
- struct domain *d = v->domain;
- u32 gflags = guest_l1e_get_flags(gw->eff_l1e);
- u32 sflags = sh_propagate_flags(v, gmfn, gflags, gw->l1e, gw->l1mfn,
- mmio, 1, ft_demand_read);
-
- if ( shadow_mode_trap_reads(d) && !mmio && sh_mfn_is_a_page_table(gmfn) )
- {
- // emulation required!
- *sl1p = shadow_l1e_empty();
- return 1;
- }
-
- *sl1p = shadow_l1e_from_mfn(gmfn, sflags);
-
- SHADOW_DEBUG(PROPAGATE,
- "va=%p eff_gl1e=%" SH_PRI_gpte " sl1e=%" SH_PRI_pte "\n",
- (void *)gw->va, gw->eff_l1e.l1, sl1p->l1);
-
- ASSERT(sflags != -1);
- return 0;
-}
-
-static inline int
-l1e_write_fault(struct vcpu *v, walk_t *gw, mfn_t gmfn, shadow_l1e_t *sl1p,
- int mmio)
-/* returns 1 if emulation is required, and 0 otherwise */
-{
- struct domain *d = v->domain;
- u32 gflags = guest_l1e_get_flags(gw->eff_l1e);
- u32 sflags = sh_propagate_flags(v, gmfn, gflags, gw->l1e, gw->l1mfn,
- mmio, 1, ft_demand_write);
-
- sh_mark_dirty(d, gmfn);
-
- if ( !mmio && sh_mfn_is_a_page_table(gmfn) )
- {
- // emulation required!
- *sl1p = shadow_l1e_empty();
- return 1;
- }
-
- *sl1p = shadow_l1e_from_mfn(gmfn, sflags);
-
- SHADOW_DEBUG(PROPAGATE,
- "va=%p eff_gl1e=%" SH_PRI_gpte " sl1e=%" SH_PRI_pte "\n",
- (void *)gw->va, gw->eff_l1e.l1, sl1p->l1);
-
- ASSERT(sflags != -1);
- return 0;
+ _sh_propagate(v, gl2e, gl2mfn, sl1mfn, sl2e, 2, ft, 0);
}
-static inline void
-l1e_propagate_from_guest(struct vcpu *v, guest_l1e_t gl1e, shadow_l1e_t *sl1p,
+static void
+l1e_propagate_from_guest(struct vcpu *v,
+ guest_l1e_t *gl1e,
+ mfn_t gl1mfn,
+ mfn_t gmfn,
+ shadow_l1e_t *sl1e,
+ fetch_type_t ft,
int mmio)
{
- gfn_t gfn = guest_l1e_get_gfn(gl1e);
- mfn_t gmfn = (mmio) ? _mfn(gfn_x(gfn)) : vcpu_gfn_to_mfn(v, gfn);
- u32 gflags = guest_l1e_get_flags(gl1e);
- u32 sflags = sh_propagate_flags(v, gmfn, gflags, 0, _mfn(INVALID_MFN),
- mmio, 1, ft_prefetch);
-
- *sl1p = shadow_l1e_from_mfn(gmfn, sflags);
-
- SHADOW_DEBUG(PROPAGATE,
- "gl1e=%" SH_PRI_gpte " sl1e=%" SH_PRI_pte "\n",
- gl1e.l1, sl1p->l1);
-
- ASSERT(sflags != -1);
+ _sh_propagate(v, gl1e, gl1mfn, gmfn, sl1e, 1, ft, mmio);
}
@@ -960,8 +844,6 @@ l1e_propagate_from_guest(struct vcpu *v, guest_l1e_t gl1e, shadow_l1e_t *sl1p,
* SHADOW_SET_FLUSH -- the caller must cause a TLB flush.
* SHADOW_SET_ERROR -- the input is not a valid entry (for example, if
* shadow_get_page_from_l1e() fails).
- * SHADOW_SET_L3PAE_RECOPY -- one or more vcpu's need to have their local
- * copies of their PAE L3 entries re-copied.
*/
static inline void safe_write_entry(void *dst, void *src)
@@ -1045,16 +927,13 @@ shadow_get_page_from_l1e(shadow_l1e_t sl1e, struct domain *d)
int res;
mfn_t mfn;
struct domain *owner;
- shadow_l1e_t sanitized_sl1e =
- shadow_l1e_remove_flags(sl1e, _PAGE_SHADOW_RW | _PAGE_SHADOW_PRESENT);
- //ASSERT(shadow_l1e_get_flags(sl1e) & _PAGE_PRESENT);
- //ASSERT((shadow_l1e_get_flags(sl1e) & L1_DISALLOW_MASK) == 0);
+ ASSERT(!sh_l1e_is_magic(sl1e));
if ( !shadow_mode_refcounts(d) )
return 1;
- res = get_page_from_l1e(sanitized_sl1e, d);
+ res = get_page_from_l1e(sl1e, d);
// If a privileged domain is attempting to install a map of a page it does
// not own, we let it succeed anyway.
@@ -1066,7 +945,7 @@ shadow_get_page_from_l1e(shadow_l1e_t sl1e, struct domain *d)
(owner = page_get_owner(mfn_to_page(mfn))) &&
(d != owner) )
{
- res = get_page_from_l1e(sanitized_sl1e, owner);
+ res = get_page_from_l1e(sl1e, owner);
SHADOW_PRINTK("privileged domain %d installs map of mfn %05lx "
"which is owned by domain %d: %s\n",
d->domain_id, mfn_x(mfn), owner->domain_id,
@@ -1254,7 +1133,8 @@ static int shadow_set_l1e(struct vcpu *v,
if ( old_sl1e.l1 == new_sl1e.l1 ) return 0; /* Nothing to do */
- if ( shadow_l1e_get_flags(new_sl1e) & _PAGE_PRESENT )
+ if ( (shadow_l1e_get_flags(new_sl1e) & _PAGE_PRESENT)
+ && !sh_l1e_is_magic(new_sl1e) )
{
/* About to install a new reference */
if ( shadow_mode_refcounts(d) ) {
@@ -1271,7 +1151,8 @@ static int shadow_set_l1e(struct vcpu *v,
shadow_write_entries(sl1e, &new_sl1e, 1, sl1mfn);
flags |= SHADOW_SET_CHANGED;
- if ( shadow_l1e_get_flags(old_sl1e) & _PAGE_PRESENT )
+ if ( (shadow_l1e_get_flags(old_sl1e) & _PAGE_PRESENT)
+ && !sh_l1e_is_magic(old_sl1e) )
{
/* We lost a reference to an old mfn. */
/* N.B. Unlike higher-level sets, never need an extra flush
@@ -2137,7 +2018,8 @@ void sh_destroy_l1_shadow(struct vcpu *v, mfn_t smfn)
/* Decrement refcounts of all the old entries */
mfn_t sl1mfn = smfn;
SHADOW_FOREACH_L1E(sl1mfn, sl1e, 0, 0, {
- if ( shadow_l1e_get_flags(*sl1e) & _PAGE_PRESENT )
+ if ( (shadow_l1e_get_flags(*sl1e) & _PAGE_PRESENT)
+ && !sh_l1e_is_magic(*sl1e) )
shadow_put_page_from_l1e(*sl1e, d);
});
}
@@ -2403,16 +2285,17 @@ static int validate_gl1e(struct vcpu *v, void *new_ge, mfn_t sl1mfn, void *se)
guest_l1e_t *new_gl1e = new_ge;
shadow_l1e_t *sl1p = se;
gfn_t gfn;
- mfn_t mfn;
- int result = 0;
+ mfn_t gmfn;
+ int result = 0, mmio;
perfc_incrc(shadow_validate_gl1e_calls);
gfn = guest_l1e_get_gfn(*new_gl1e);
- mfn = vcpu_gfn_to_mfn(v, gfn);
+ gmfn = vcpu_gfn_to_mfn(v, gfn);
- l1e_propagate_from_guest(v, *new_gl1e, &new_sl1e,
- /* mmio? */ !valid_mfn(mfn));
+ mmio = (is_hvm_vcpu(v) && shadow_vcpu_mode_translate(v) && !valid_mfn(gmfn));
+ l1e_propagate_from_guest(v, new_gl1e, _mfn(INVALID_MFN), gmfn, &new_sl1e,
+ ft_prefetch, mmio);
result |= shadow_set_l1e(v, sl1p, new_sl1e, sl1mfn);
return result;
@@ -2583,6 +2466,80 @@ static inline void reset_early_unshadow(struct vcpu *v)
/**************************************************************************/
+/* Optimization: Prefetch multiple L1 entries. This is called after we have
+ * demand-faulted a shadow l1e in the fault handler, to see if it's
+ * worth fetching some more.
+ */
+
+#if SHADOW_OPTIMIZATIONS & SHOPT_PREFETCH
+
+/* XXX magic number */
+#define PREFETCH_DISTANCE 32
+
+static void sh_prefetch(struct vcpu *v, walk_t *gw,
+ shadow_l1e_t *ptr_sl1e, mfn_t sl1mfn)
+{
+ int i, dist, mmio;
+ gfn_t gfn;
+ mfn_t gmfn;
+ guest_l1e_t gl1e;
+ shadow_l1e_t sl1e;
+ u32 gflags;
+
+ /* Prefetch no further than the end of the _shadow_ l1 MFN */
+ dist = (PAGE_SIZE - ((unsigned long)ptr_sl1e & ~PAGE_MASK)) / sizeof sl1e;
+ /* And no more than a maximum fetches-per-fault */
+ if ( dist > PREFETCH_DISTANCE )
+ dist = PREFETCH_DISTANCE;
+
+ for ( i = 1; i < dist ; i++ )
+ {
+ /* No point in prefetching if there's already a shadow */
+ if ( ptr_sl1e[i].l1 != 0 )
+ break;
+
+ if ( gw->l1e )
+ {
+ /* Normal guest page; grab the next guest entry */
+ gl1e = gw->l1e[i];
+ /* Not worth continuing if we hit an entry that will need another
+ * fault for A/D-bit propagation anyway */
+ gflags = guest_l1e_get_flags(gl1e);
+ if ( (gflags & _PAGE_PRESENT)
+ && (!(gflags & _PAGE_ACCESSED)
+ || ((gflags & _PAGE_RW) && !(gflags & _PAGE_DIRTY))) )
+ break;
+ }
+ else
+ {
+ /* Fragmented superpage, unless we've been called wrongly */
+ ASSERT(guest_l2e_get_flags(*gw->l2e) & _PAGE_PSE);
+ /* Increment the l1e's GFN by the right number of guest pages */
+ gl1e = guest_l1e_from_gfn(
+ _gfn(gfn_x(guest_l1e_get_gfn(gw->eff_l1e)) + i),
+ guest_l1e_get_flags(gw->eff_l1e));
+ }
+
+ /* Look at the gfn that the l1e is pointing at */
+ gfn = guest_l1e_get_gfn(gl1e);
+ gmfn = vcpu_gfn_to_mfn(v, gfn);
+ mmio = ( is_hvm_vcpu(v)
+ && shadow_vcpu_mode_translate(v)
+ && mmio_space(gfn_to_paddr(gfn)) );
+
+ /* Propagate the entry. Safe to use a pointer to our local
+ * gl1e, since this is not a demand-fetch so there will be no
+ * write-back to the guest. */
+ l1e_propagate_from_guest(v, &gl1e, _mfn(INVALID_MFN),
+ gmfn, &sl1e, ft_prefetch, mmio);
+ (void) shadow_set_l1e(v, ptr_sl1e + i, sl1e, sl1mfn);
+ }
+}
+
+#endif /* SHADOW_OPTIMIZATIONS & SHOPT_PREFETCH */
+
+
+/**************************************************************************/
/* Entry points into the shadow code */
/* Called from pagefault handler in Xen, and from the HVM trap handlers
@@ -2606,16 +2563,71 @@ static int sh_page_fault(struct vcpu *v,
int r, mmio;
fetch_type_t ft = 0;
+ SHADOW_PRINTK("d:v=%u:%u va=%#lx err=%u\n",
+ v->domain->domain_id, v->vcpu_id, va, regs->error_code);
+
//
// XXX: Need to think about eventually mapping superpages directly in the
// shadow (when possible), as opposed to splintering them into a
// bunch of 4K maps.
//
- shadow_lock(d);
+#if (SHADOW_OPTIMIZATIONS & SHOPT_FAST_FAULT_PATH) && SHADOW_PAGING_LEVELS > 2
+ if ( (regs->error_code & PFEC_reserved_bit) )
+ {
+ /* The only reasons for reserved bits to be set in shadow entries
+ * are the two "magic" shadow_l1e entries. */
+ if ( likely((__copy_from_user(&sl1e,
+ (sh_linear_l1_table(v)
+ + shadow_l1_linear_offset(va)),
+ sizeof(sl1e)) == 0)
+ && sh_l1e_is_magic(sl1e)) )
+ {
+ if ( sh_l1e_is_gnp(sl1e) )
+ {
+ if ( likely(!is_hvm_domain(d) ||
+ shadow_vcpu_mode_translate(v)) )
+ {
+ /* Not-present in a guest PT: pass to the guest as
+ * a not-present fault (by flipping two bits). */
+ ASSERT(regs->error_code & PFEC_page_present);
+ regs->error_code ^= (PFEC_reserved_bit|PFEC_page_present);
+ perfc_incrc(shadow_fault_fast_gnp);
+ SHADOW_PRINTK("fast path not-present\n");
+ return 0;
+ }
+ else
+ {
+ /* Not-present in the P2M: MMIO */
+ gpa = va;
+ }
+ }
+ else
+ {
+ /* Magic MMIO marker: extract gfn for MMIO address */
+ ASSERT(sh_l1e_is_mmio(sl1e));
+ gpa = (((paddr_t)(gfn_x(sh_l1e_mmio_get_gfn(sl1e))))
+ << PAGE_SHIFT)
+ | (va & ~PAGE_MASK);
+ }
+ perfc_incrc(shadow_fault_fast_mmio);
+ SHADOW_PRINTK("fast path mmio %#"PRIpaddr"\n", gpa);
+ reset_early_unshadow(v);
+ handle_mmio(gpa);
+ return EXCRET_fault_fixed;
+ }
+ else
+ {
+ /* This should be exceptionally rare: another vcpu has fixed
+ * the tables between the fault and our reading the l1e.
+ * Fall through to the normal fault handing logic */
+ perfc_incrc(shadow_fault_fast_fail);
+ SHADOW_PRINTK("fast path false alarm!\n");
+ }
+ }
+#endif /* SHOPT_FAST_FAULT_PATH */
- SHADOW_PRINTK("d:v=%u:%u va=%#lx err=%u\n",
- v->domain->domain_id, v->vcpu_id, va, regs->error_code);
+ shadow_lock(d);
shadow_audit_tables(v);
@@ -2636,7 +2648,7 @@ static int sh_page_fault(struct vcpu *v,
//
if ( unlikely(!(guest_l1e_get_flags(gw.eff_l1e) & _PAGE_PRESENT)) )
{
- if ( hvm_guest(v) && !shadow_vcpu_mode_translate(v) )
+ if ( is_hvm_domain(d) && !shadow_vcpu_mode_translate(v) )
{
/* Not present in p2m map, means this is mmio */
gpa = va;
@@ -2663,8 +2675,9 @@ static int sh_page_fault(struct vcpu *v,
}
// Was it a write fault?
- //
- if ( regs->error_code & PFEC_write_access )
+ ft = ((regs->error_code & PFEC_write_access)
+ ? ft_demand_write : ft_demand_read);
+ if ( ft == ft_demand_write )
{
if ( unlikely(!(accumulated_gflags & _PAGE_RW)) )
{
@@ -2689,26 +2702,19 @@ static int sh_page_fault(struct vcpu *v,
}
}
- /* Is this an MMIO access? */
+ /* What mfn is the guest trying to access? */
gfn = guest_l1e_get_gfn(gw.eff_l1e);
- mmio = ( hvm_guest(v)
- && shadow_vcpu_mode_translate(v)
- && mmio_space(gfn_to_paddr(gfn)) );
+ gmfn = vcpu_gfn_to_mfn(v, gfn);
+ mmio = (is_hvm_domain(d)
+ && shadow_vcpu_mode_translate(v)
+ && mmio_space(gfn_to_paddr(gfn)));
- /* For MMIO, the shadow holds the *gfn*; for normal accesses, it holds
- * the equivalent mfn. */
- if ( mmio )
- gmfn = _mfn(gfn_x(gfn));
- else
+ if ( !mmio && !valid_mfn(gmfn) )
{
- gmfn = vcpu_gfn_to_mfn(v, gfn);
- if ( !valid_mfn(gmfn) )
- {
- perfc_incrc(shadow_fault_bail_bad_gfn);
- SHADOW_PRINTK("BAD gfn=%"SH_PRI_gfn" gmfn=%"SH_PRI_mfn"\n",
- gfn_x(gfn), mfn_x(gmfn));
- goto not_a_shadow_fault;
- }
+ perfc_incrc(shadow_fault_bail_bad_gfn);
+ SHADOW_PRINTK("BAD gfn=%"SH_PRI_gfn" gmfn=%"SH_PRI_mfn"\n",
+ gfn_x(gfn), mfn_x(gmfn));
+ goto not_a_shadow_fault;
}
/* Make sure there is enough free shadow memory to build a chain of
@@ -2721,31 +2727,33 @@ static int sh_page_fault(struct vcpu *v,
* for the shadow entry, since we might promote a page here. */
// XXX -- this code will need to change somewhat if/when the shadow code
// can directly map superpages...
- ft = ((regs->error_code & PFEC_write_access) ?
- ft_demand_write : ft_demand_read);
ptr_sl1e = shadow_get_and_create_l1e(v, &gw, &sl1mfn, ft);
ASSERT(ptr_sl1e);
- /* Calculate the shadow entry */
- if ( ft == ft_demand_write )
+ /* Calculate the shadow entry and write it */
+ l1e_propagate_from_guest(v, (gw.l1e) ? gw.l1e : &gw.eff_l1e, gw.l1mfn,
+ gmfn, &sl1e, ft, mmio);
+ r = shadow_set_l1e(v, ptr_sl1e, sl1e, sl1mfn);
+
+#if SHADOW_OPTIMIZATIONS & SHOPT_PREFETCH
+ /* Prefetch some more shadow entries */
+ sh_prefetch(v, &gw, ptr_sl1e, sl1mfn);
+#endif
+
+ /* Need to emulate accesses to page tables */
+ if ( sh_mfn_is_a_page_table(gmfn) )
{
- if ( l1e_write_fault(v, &gw, gmfn, &sl1e, mmio) )
+ if ( ft == ft_demand_write )
{
perfc_incrc(shadow_fault_emulate_write);
goto emulate;
}
+ else if ( shadow_mode_trap_reads(d) && ft == ft_demand_read )
+ {
+ perfc_incrc(shadow_fault_emulate_read);
+ goto emulate;
+ }
}
- else if ( l1e_read_fault(v, &gw, gmfn, &sl1e, mmio) )
- {
- perfc_incrc(shadow_fault_emulate_read);
- goto emulate;
- }
-
- /* Quick sanity check: we never make an MMIO entry that's got the
- * _PAGE_PRESENT flag set in it. */
- ASSERT(!mmio || !(shadow_l1e_get_flags(sl1e) & _PAGE_PRESENT));
-
- r = shadow_set_l1e(v, ptr_sl1e, sl1e, sl1mfn);
if ( mmio )
{
@@ -2753,13 +2761,6 @@ static int sh_page_fault(struct vcpu *v,
goto mmio;
}
-#if 0
- if ( !(r & SHADOW_SET_CHANGED) )
- debugtrace_printk("%s: shadow_set_l1e(va=%p, sl1e=%" SH_PRI_pte
- ") did not change anything\n",
- __func__, gw.va, l1e_get_intpte(sl1e));
-#endif
-
perfc_incrc(shadow_fault_fixed);
d->arch.shadow.fault_count++;
reset_early_unshadow(v);
@@ -2773,17 +2774,17 @@ static int sh_page_fault(struct vcpu *v,
return EXCRET_fault_fixed;
emulate:
-
/* Take the register set we were called with */
emul_regs = *regs;
- if ( hvm_guest(v) )
+ if ( is_hvm_domain(d) )
{
/* Add the guest's segment selectors, rip, rsp. rflags */
hvm_store_cpu_guest_regs(v, &emul_regs, NULL);
}
emul_ctxt.regs = &emul_regs;
emul_ctxt.cr2 = va;
- emul_ctxt.mode = hvm_guest(v) ? hvm_guest_x86_mode(v) : X86EMUL_MODE_HOST;
+ emul_ctxt.mode = (is_hvm_domain(d) ?
+ hvm_guest_x86_mode(v) : X86EMUL_MODE_HOST);
SHADOW_PRINTK("emulate: eip=%#lx\n", emul_regs.eip);
@@ -2814,7 +2815,7 @@ static int sh_page_fault(struct vcpu *v,
goto not_a_shadow_fault;
/* Emulator has changed the user registers: write back */
- if ( hvm_guest(v) )
+ if ( is_hvm_domain(d) )
{
/* Write back the guest's segment selectors, rip, rsp. rflags */
hvm_load_cpu_guest_regs(v, &emul_regs);
@@ -2837,7 +2838,7 @@ static int sh_page_fault(struct vcpu *v,
perfc_incrc(shadow_fault_mmio);
sh_audit_gw(v, &gw);
unmap_walk(v, &gw);
- SHADOW_PRINTK("mmio\n");
+ SHADOW_PRINTK("mmio %#"PRIpaddr"\n", gpa);
shadow_audit_tables(v);
reset_early_unshadow(v);
shadow_unlock(d);
@@ -2941,7 +2942,7 @@ sh_gva_to_gfn(struct vcpu *v, unsigned long va)
}
-static unsigned long
+static paddr_t
sh_gva_to_gpa(struct vcpu *v, unsigned long va)
/* Called to translate a guest virtual address to what the *guest*
* pagetables would map it to. */
@@ -2950,7 +2951,7 @@ sh_gva_to_gpa(struct vcpu *v, unsigned long va)
if ( gfn == INVALID_GFN )
return 0;
else
- return (gfn << PAGE_SHIFT) | (va & ~PAGE_MASK);
+ return (((paddr_t)gfn) << PAGE_SHIFT) + (va & ~PAGE_MASK);
}
@@ -3318,7 +3319,7 @@ sh_update_cr3(struct vcpu *v)
#ifndef NDEBUG
/* Double-check that the HVM code has sent us a sane guest_table */
- if ( hvm_guest(v) )
+ if ( is_hvm_domain(d) )
{
gfn_t gfn;
@@ -3356,7 +3357,7 @@ sh_update_cr3(struct vcpu *v)
sh_detach_old_tables(v);
- if ( !test_bit(_VCPUF_initialised, &v->vcpu_flags) )
+ if ( !is_hvm_domain(d) && !test_bit(_VCPUF_initialised, &v->vcpu_flags) )
{
ASSERT(v->arch.cr3 == 0);
return;
@@ -3493,7 +3494,7 @@ sh_update_cr3(struct vcpu *v)
///
if ( shadow_mode_external(d) )
{
- ASSERT(hvm_guest(v));
+ ASSERT(is_hvm_domain(d));
#if SHADOW_PAGING_LEVELS == 3
/* 2-on-3 or 3-on-3: Use the PAE shadow l3 table we just fabricated */
v->arch.hvm_vcpu.hw_cr3 = virt_to_maddr(&v->arch.shadow.l3table);
@@ -3891,7 +3892,7 @@ static char * sh_audit_flags(struct vcpu *v, int level,
{
if ( (sflags & _PAGE_PRESENT) && !(gflags & _PAGE_PRESENT) )
return "shadow is present but guest is not present";
- if ( (sflags & _PAGE_GLOBAL) && !hvm_guest(v) )
+ if ( (sflags & _PAGE_GLOBAL) && !is_hvm_vcpu(v) )
return "global bit set in PV shadow";
if ( (level == 1 || (level == 2 && (gflags & _PAGE_PSE)))
&& ((sflags & _PAGE_DIRTY) && !(gflags & _PAGE_DIRTY)) )
@@ -3936,25 +3937,48 @@ int sh_audit_l1_table(struct vcpu *v, mfn_t sl1mfn, mfn_t x)
gfn_t gfn;
char *s;
int done = 0;
-
+
/* Follow the backpointer */
gl1mfn = _mfn(mfn_to_page(sl1mfn)->u.inuse.type_info);
gl1e = gp = sh_map_domain_page(gl1mfn);
SHADOW_FOREACH_L1E(sl1mfn, sl1e, &gl1e, done, {
- s = sh_audit_flags(v, 1, guest_l1e_get_flags(*gl1e),
- shadow_l1e_get_flags(*sl1e));
- if ( s ) AUDIT_FAIL(1, "%s", s);
-
- if ( SHADOW_AUDIT & SHADOW_AUDIT_ENTRIES_MFNS )
+ if ( sh_l1e_is_magic(*sl1e) )
{
- gfn = guest_l1e_get_gfn(*gl1e);
- mfn = shadow_l1e_get_mfn(*sl1e);
- gmfn = audit_gfn_to_mfn(v, gfn, gl1mfn);
- if ( mfn_x(gmfn) != mfn_x(mfn) )
- AUDIT_FAIL(1, "bad translation: gfn %" SH_PRI_gfn
- " --> %" SH_PRI_mfn " != mfn %" SH_PRI_mfn "\n",
- gfn_x(gfn), mfn_x(gmfn), mfn_x(mfn));
+#if (SHADOW_OPTIMIZATIONS & SHOPT_FAST_FAULT_PATH) && SHADOW_PAGING_LEVELS > 2
+ if ( sh_l1e_is_gnp(*sl1e) )
+ {
+ if ( guest_l1e_get_flags(*gl1e) & _PAGE_PRESENT )
+ AUDIT_FAIL(1, "shadow is GNP magic but guest is present");
+ }
+ else
+ {
+ ASSERT(sh_l1e_is_mmio(*sl1e));
+ gfn = sh_l1e_mmio_get_gfn(*sl1e);
+ if ( gfn_x(gfn) != gfn_x(guest_l1e_get_gfn(*gl1e)) )
+ AUDIT_FAIL(1, "shadow MMIO gfn is %" SH_PRI_gfn
+ " but guest gfn is %" SH_PRI_gfn,
+ gfn_x(gfn),
+ gfn_x(guest_l1e_get_gfn(*gl1e)));
+ }
+#endif
+ }
+ else
+ {
+ s = sh_audit_flags(v, 1, guest_l1e_get_flags(*gl1e),
+ shadow_l1e_get_flags(*sl1e));
+ if ( s ) AUDIT_FAIL(1, "%s", s);
+
+ if ( SHADOW_AUDIT & SHADOW_AUDIT_ENTRIES_MFNS )
+ {
+ gfn = guest_l1e_get_gfn(*gl1e);
+ mfn = shadow_l1e_get_mfn(*sl1e);
+ gmfn = audit_gfn_to_mfn(v, gfn, gl1mfn);
+ if ( mfn_x(gmfn) != mfn_x(mfn) )
+ AUDIT_FAIL(1, "bad translation: gfn %" SH_PRI_gfn
+ " --> %" SH_PRI_mfn " != mfn %" SH_PRI_mfn,
+ gfn_x(gfn), mfn_x(gmfn), mfn_x(mfn));
+ }
}
});
sh_unmap_domain_page(gp);
@@ -3977,7 +4001,8 @@ int sh_audit_fl1_table(struct vcpu *v, mfn_t sl1mfn, mfn_t x)
if ( !(f == 0
|| f == (_PAGE_PRESENT|_PAGE_USER|_PAGE_RW|
_PAGE_ACCESSED|_PAGE_DIRTY)
- || f == (_PAGE_PRESENT|_PAGE_USER|_PAGE_ACCESSED|_PAGE_DIRTY)) )
+ || f == (_PAGE_PRESENT|_PAGE_USER|_PAGE_ACCESSED|_PAGE_DIRTY)
+ || sh_l1e_is_magic(*sl1e)) )
AUDIT_FAIL(1, "fl1e has bad flags");
});
return 0;
@@ -4015,7 +4040,7 @@ int sh_audit_l2_table(struct vcpu *v, mfn_t sl2mfn, mfn_t x)
if ( mfn_x(gmfn) != mfn_x(mfn) )
AUDIT_FAIL(2, "bad translation: gfn %" SH_PRI_gfn
" (--> %" SH_PRI_mfn ")"
- " --> %" SH_PRI_mfn " != mfn %" SH_PRI_mfn "\n",
+ " --> %" SH_PRI_mfn " != mfn %" SH_PRI_mfn,
gfn_x(gfn),
(guest_l2e_get_flags(*gl2e) & _PAGE_PSE) ? 0
: mfn_x(audit_gfn_to_mfn(v, gfn, gl2mfn)),
@@ -4057,7 +4082,7 @@ int sh_audit_l3_table(struct vcpu *v, mfn_t sl3mfn, mfn_t x)
: PGC_SH_l2_shadow);
if ( mfn_x(gmfn) != mfn_x(mfn) )
AUDIT_FAIL(3, "bad translation: gfn %" SH_PRI_gfn
- " --> %" SH_PRI_mfn " != mfn %" SH_PRI_mfn "\n",
+ " --> %" SH_PRI_mfn " != mfn %" SH_PRI_mfn,
gfn_x(gfn), mfn_x(gmfn), mfn_x(mfn));
}
});
@@ -4092,7 +4117,7 @@ int sh_audit_l4_table(struct vcpu *v, mfn_t sl4mfn, mfn_t x)
PGC_SH_l3_shadow);
if ( mfn_x(gmfn) != mfn_x(mfn) )
AUDIT_FAIL(4, "bad translation: gfn %" SH_PRI_gfn
- " --> %" SH_PRI_mfn " != mfn %" SH_PRI_mfn "\n",
+ " --> %" SH_PRI_mfn " != mfn %" SH_PRI_mfn,
gfn_x(gfn), mfn_x(gmfn), mfn_x(mfn));
}
});
diff --git a/xen/arch/x86/mm/shadow/private.h b/xen/arch/x86/mm/shadow/private.h
index ba56d2b809..87b7f1473c 100644
--- a/xen/arch/x86/mm/shadow/private.h
+++ b/xen/arch/x86/mm/shadow/private.h
@@ -33,111 +33,6 @@
/******************************************************************************
- * Definitions for the use of the "available" bits in the shadow PTEs.
- *
- * Review of the low 12 bits of a shadow page table entry:
- *
- * in a guest: in a shadow:
- * Bit 11: _PAGE_AVAIL2, aka _PAGE_GNTTAB
- * Bit 10: _PAGE_AVAIL1 _PAGE_SHADOW_RW ("SW" below)
- * Bit 9: _PAGE_AVAIL0 _PAGE_SHADOW_PRESENT ("SP" below)
- * Bit 8: _PAGE_GLOBAL _PAGE_SHADOW_MMIO ("MMIO" below),
- * aka _PAGE_SHADOW_GUEST_NOT_PRESENT
- * Bit 7: _PAGE_PSE, aka _PAGE_PAT
- * Bit 6: _PAGE_DIRTY
- * Bit 5: _PAGE_ACCESSED
- * Bit 4: _PAGE_PCD
- * Bit 3: _PAGE_PWT
- * Bit 2: _PAGE_USER
- * Bit 1: _PAGE_RW ("GW" below)
- * Bit 0: _PAGE_PRESENT ("GP" below)
- *
- * Given a guest entry, as shown below, we can expect the following in the
- * corresponding shadow entry:
- *
- * Guest entry Shadow entry Commentary
- * ----------- ---------------- ---------------------------------------------
- * Maps
- * GP GW IO GP SP GW SW MMIO
- * -- -- ---- -- -- -- -- ----
- * - - - 0 0 0 0 0 The guest entry has not yet been shadowed.
- * 0 - - 0 0 0 0 1 The guest entry is marked not-present.
- * 1 1 no ? 1 ? 1 0 Writable entry in the guest.
- * 1 0 no ? 1 0 0 0 Read-only entry in the guest.
- * 1 1 yes 0 1 ? 1 1 Writable MMIO mapping in the guest.
- * 1 0 yes 0 1 0 0 1 Read-only MMIO mapping in the guest.
- *
- * Normally, we would expect that GP=1 in the guest to imply GP=1 in the
- * shadow, and similarly for GW=1. However, various functionality that may be
- * implemented via the shadow can cause GP or GW to be cleared in such cases.
- * A & D bit emulation is a prime example of such functionality.
- *
- * If _PAGE_SHADOW_PRESENT is zero, then the _PAGE_PRESENT bit in that same
- * entry will always be zero, too.
-
- * Bit 11 is used in debug builds as the _PAGE_GNTTAB bit in PV guests. It is
- * currently available for random (ab)use in shadow entries.
- *
- * Bit 8 (the global bit) could be propagated from an HVM guest to the shadow,
- * but currently there is no benefit, as the guest's TLB is flushed on every
- * transition of CR3 anyway due to the HVM exit/re-entry.
- *
- * In shadow entries in which the _PAGE_SHADOW_PRESENT is set, bit 8 is used
- * as the _PAGE_SHADOW_MMIO bit. In such entries, if _PAGE_SHADOW_MMIO is
- * set, then the entry contains the *gfn* directly from the corresponding
- * guest entry (not an mfn!!).
- *
- * Bit 7 is set in a guest L2 to signify a superpage entry. The current
- * shadow code splinters superpage mappings into 512 or 1024 4K mappings; the
- * resulting shadow L1 table is called an FL1. Note that there is no guest
- * page that corresponds to an FL1.
- *
- * Bit 7 in a guest L1 is the PAT2 bit. Currently we do not support PAT in
- * this shadow code.
- *
- * Bit 6 is the dirty bit.
- *
- * Bit 5 is the accessed bit.
- *
- * Bit 4 is the cache disable bit. If set in a guest, the hardware is
- * supposed to refuse to cache anything found via this entry. It can be set
- * in an L4e, L3e, L2e, or L1e. This shadow code currently does not support
- * cache disable bits. They are silently ignored.
- *
- * Bit 4 is a guest L1 is also the PAT1 bit. Currently we do not support PAT
- * in this shadow code.
- *
- * Bit 3 is the cache write-thru bit. If set in a guest, the hardware is
- * supposed to use write-thru instead of write-back caching for anything found
- * via this entry. It can be set in an L4e, L3e, L2e, or L1e. This shadow
- * code currently does not support cache write-thru bits. They are silently
- * ignored.
- *
- * Bit 3 is a guest L1 is also the PAT0 bit. Currently we do not support PAT
- * in this shadow code.
- *
- * Bit 2 is the user bit.
- *
- * Bit 1 is the read-write bit.
- *
- * Bit 0 is the present bit.
- */
-
-// Copy of the _PAGE_RW bit from the guest's PTE, appropriately zero'ed by
-// the appropriate shadow rules.
-#define _PAGE_SHADOW_RW _PAGE_AVAIL1
-
-// Copy of the _PAGE_PRESENT bit from the guest's PTE
-#define _PAGE_SHADOW_PRESENT _PAGE_AVAIL0
-
-// The matching guest entry maps MMIO space
-#define _PAGE_SHADOW_MMIO _PAGE_GLOBAL
-
-// Shadow flags value used when the guest is not present
-#define _PAGE_SHADOW_GUEST_NOT_PRESENT _PAGE_GLOBAL
-
-
-/******************************************************************************
* Debug and error-message output
*/
#define SHADOW_PRINTK(_f, _a...) \
@@ -151,13 +46,13 @@
} while (0)
// The flags for use with SHADOW_DEBUG:
-#define SHADOW_DEBUG_PROPAGATE 0
-#define SHADOW_DEBUG_MAKE_SHADOW 0
-#define SHADOW_DEBUG_DESTROY_SHADOW 0
+#define SHADOW_DEBUG_PROPAGATE 1
+#define SHADOW_DEBUG_MAKE_SHADOW 1
+#define SHADOW_DEBUG_DESTROY_SHADOW 1
#define SHADOW_DEBUG_P2M 0
-#define SHADOW_DEBUG_A_AND_D 0
-#define SHADOW_DEBUG_EMULATE 0
-#define SHADOW_DEBUG_LOGDIRTY 1
+#define SHADOW_DEBUG_A_AND_D 1
+#define SHADOW_DEBUG_EMULATE 1
+#define SHADOW_DEBUG_LOGDIRTY 0
/******************************************************************************
diff --git a/xen/arch/x86/mm/shadow/types.h b/xen/arch/x86/mm/shadow/types.h
index 69d077f561..2dd2908d69 100644
--- a/xen/arch/x86/mm/shadow/types.h
+++ b/xen/arch/x86/mm/shadow/types.h
@@ -205,13 +205,13 @@ static inline shadow_l4e_t shadow_l4e_from_mfn(mfn_t mfn, u32 flags)
__sh_linear_l1_table; \
})
-// XXX -- these should not be conditional on hvm_guest(v), but rather on
+// XXX -- these should not be conditional on is_hvm_vcpu(v), but rather on
// shadow_mode_external(d)...
//
#define sh_linear_l2_table(v) ({ \
ASSERT(current == (v)); \
((shadow_l2e_t *) \
- (hvm_guest(v) ? __linear_l1_table : __sh_linear_l1_table) + \
+ (is_hvm_vcpu(v) ? __linear_l1_table : __sh_linear_l1_table) + \
shadow_l1_linear_offset(SH_LINEAR_PT_VIRT_START)); \
})
@@ -219,7 +219,7 @@ static inline shadow_l4e_t shadow_l4e_from_mfn(mfn_t mfn, u32 flags)
#define sh_linear_l3_table(v) ({ \
ASSERT(current == (v)); \
((shadow_l3e_t *) \
- (hvm_guest(v) ? __linear_l2_table : __sh_linear_l2_table) + \
+ (is_hvm_vcpu(v) ? __linear_l2_table : __sh_linear_l2_table) + \
shadow_l2_linear_offset(SH_LINEAR_PT_VIRT_START)); \
})
@@ -228,7 +228,7 @@ static inline shadow_l4e_t shadow_l4e_from_mfn(mfn_t mfn, u32 flags)
#define sh_linear_l4_table(v) ({ \
ASSERT(current == (v)); \
((l4_pgentry_t *) \
- (hvm_guest(v) ? __linear_l3_table : __sh_linear_l3_table) + \
+ (is_hvm_vcpu(v) ? __linear_l3_table : __sh_linear_l3_table) + \
shadow_l3_linear_offset(SH_LINEAR_PT_VIRT_START)); \
})
#endif
@@ -404,11 +404,22 @@ valid_gfn(gfn_t m)
}
/* Translation between mfns and gfns */
+
+// vcpu-specific version of gfn_to_mfn(). This is where we hide the dirty
+// little secret that, for hvm guests with paging disabled, nearly all of the
+// shadow code actually think that the guest is running on *untranslated* page
+// tables (which is actually domain->phys_table).
+//
+
static inline mfn_t
vcpu_gfn_to_mfn(struct vcpu *v, gfn_t gfn)
{
- return sh_vcpu_gfn_to_mfn(v, gfn_x(gfn));
-}
+ if ( !shadow_vcpu_mode_translate(v) )
+ return _mfn(gfn_x(gfn));
+ if ( likely(current->domain == v->domain) )
+ return _mfn(get_mfn_from_gpfn(gfn_x(gfn)));
+ return sh_gfn_to_mfn_foreign(v->domain, gfn_x(gfn));
+}
static inline gfn_t
mfn_to_gfn(struct domain *d, mfn_t mfn)
@@ -574,12 +585,83 @@ accumulate_guest_flags(struct vcpu *v, walk_t *gw)
// In 64-bit PV guests, the _PAGE_USER bit is implied in all guest
// entries (since even the guest kernel runs in ring 3).
//
- if ( (GUEST_PAGING_LEVELS == 4) && !hvm_guest(v) )
+ if ( (GUEST_PAGING_LEVELS == 4) && !is_hvm_vcpu(v) )
accumulated_flags |= _PAGE_USER;
return accumulated_flags;
}
+
+#if (SHADOW_OPTIMIZATIONS & SHOPT_FAST_FAULT_PATH) && SHADOW_PAGING_LEVELS > 2
+/******************************************************************************
+ * We implement a "fast path" for two special cases: faults that require
+ * MMIO emulation, and faults where the guest PTE is not present. We
+ * record these as shadow l1 entries that have reserved bits set in
+ * them, so we can spot them immediately in the fault handler and handle
+ * them without needing to hold the shadow lock or walk the guest
+ * pagetables.
+ *
+ * This is only feasible for PAE and 64bit Xen: 32-bit non-PAE PTEs don't
+ * have reserved bits that we can use for this.
+ */
+
+#define SH_L1E_MAGIC 0xffffffff00000000ULL
+static inline int sh_l1e_is_magic(shadow_l1e_t sl1e)
+{
+ return ((sl1e.l1 & SH_L1E_MAGIC) == SH_L1E_MAGIC);
+}
+
+/* Guest not present: a single magic value */
+static inline shadow_l1e_t sh_l1e_gnp(void)
+{
+ return (shadow_l1e_t){ -1ULL };
+}
+
+static inline int sh_l1e_is_gnp(shadow_l1e_t sl1e)
+{
+ return (sl1e.l1 == sh_l1e_gnp().l1);
+}
+
+/* MMIO: an invalid PTE that contains the GFN of the equivalent guest l1e.
+ * We store 28 bits of GFN in bits 4:32 of the entry.
+ * The present bit is set, and the U/S and R/W bits are taken from the guest.
+ * Bit 3 is always 0, to differentiate from gnp above. */
+#define SH_L1E_MMIO_MAGIC 0xffffffff00000001ULL
+#define SH_L1E_MMIO_MAGIC_MASK 0xffffffff00000009ULL
+#define SH_L1E_MMIO_GFN_MASK 0x00000000fffffff0ULL
+#define SH_L1E_MMIO_GFN_SHIFT 4
+
+static inline shadow_l1e_t sh_l1e_mmio(gfn_t gfn, u32 gflags)
+{
+ return (shadow_l1e_t) { (SH_L1E_MMIO_MAGIC
+ | (gfn_x(gfn) << SH_L1E_MMIO_GFN_SHIFT)
+ | (gflags & (_PAGE_USER|_PAGE_RW))) };
+}
+
+static inline int sh_l1e_is_mmio(shadow_l1e_t sl1e)
+{
+ return ((sl1e.l1 & SH_L1E_MMIO_MAGIC_MASK) == SH_L1E_MMIO_MAGIC);
+}
+
+static inline gfn_t sh_l1e_mmio_get_gfn(shadow_l1e_t sl1e)
+{
+ return _gfn((sl1e.l1 & SH_L1E_MMIO_GFN_MASK) >> SH_L1E_MMIO_GFN_SHIFT);
+}
+
+static inline u32 sh_l1e_mmio_get_flags(shadow_l1e_t sl1e)
+{
+ return (u32)((sl1e.l1 & (_PAGE_USER|_PAGE_RW)));
+}
+
+#else
+
+#define sh_l1e_gnp() shadow_l1e_empty()
+#define sh_l1e_mmio(_gfn, _flags) shadow_l1e_empty()
+#define sh_l1e_is_magic(_e) (0)
+
+#endif /* SHOPT_FAST_FAULT_PATH */
+
+
#endif /* _XEN_SHADOW_TYPES_H */
/*
diff --git a/xen/arch/x86/oprofile/xenoprof.c b/xen/arch/x86/oprofile/xenoprof.c
index 27c2a90297..288e7b2f74 100644
--- a/xen/arch/x86/oprofile/xenoprof.c
+++ b/xen/arch/x86/oprofile/xenoprof.c
@@ -701,7 +701,7 @@ int xenoprofile_get_mode(struct vcpu *v, struct cpu_user_regs * const regs)
if ( !guest_mode(regs) )
return 2;
- if ( hvm_guest(v) )
+ if ( is_hvm_vcpu(v) )
return ((regs->cs & 3) != 3);
return guest_kernel_mode(v, regs);
diff --git a/xen/arch/x86/platform_hypercall.c b/xen/arch/x86/platform_hypercall.c
index 33be6746b3..7d2ee6f496 100644
--- a/xen/arch/x86/platform_hypercall.c
+++ b/xen/arch/x86/platform_hypercall.c
@@ -125,7 +125,7 @@ long do_platform_op(XEN_GUEST_HANDLE(xen_platform_op_t) u_xenpf_op)
case QUIRK_IOAPIC_GOOD_REGSEL:
#ifndef sis_apic_bug
sis_apic_bug = (quirk_id == QUIRK_IOAPIC_BAD_REGSEL);
- DPRINTK("Domain 0 says that IO-APIC REGSEL is %s\n",
+ dprintk(XENLOG_INFO, "Domain 0 says that IO-APIC REGSEL is %s\n",
sis_apic_bug ? "bad" : "good");
#else
BUG_ON(sis_apic_bug != (quirk_id == QUIRK_IOAPIC_BAD_REGSEL));
diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c
index 15c42b133c..e144b6ca47 100644
--- a/xen/arch/x86/setup.c
+++ b/xen/arch/x86/setup.c
@@ -249,7 +249,7 @@ static void __init init_idle_domain(void)
/* Domain creation requires that scheduler structures are initialised. */
scheduler_init();
- idle_domain = domain_create(IDLE_DOMAIN_ID);
+ idle_domain = domain_create(IDLE_DOMAIN_ID, 0);
if ( (idle_domain == NULL) || (alloc_vcpu(idle_domain, 0, 0) == NULL) )
BUG();
@@ -363,7 +363,7 @@ void __init __start_xen(multiboot_info_t *mbi)
e820_raw[e820_raw_nr].size =
((u64)map->length_high << 32) | (u64)map->length_low;
e820_raw[e820_raw_nr].type =
- (map->type > E820_SHARED_PAGE) ? E820_RESERVED : map->type;
+ (map->type > E820_NVS) ? E820_RESERVED : map->type;
e820_raw_nr++;
bytes += map->size + 4;
@@ -640,12 +640,13 @@ void __init __start_xen(multiboot_info_t *mbi)
acm_init(_policy_start, _policy_len);
/* Create initial domain 0. */
- dom0 = domain_create(0);
+ dom0 = domain_create(0, 0);
if ( (dom0 == NULL) || (alloc_vcpu(dom0, 0, 0) == NULL) )
panic("Error creating domain 0\n");
- set_bit(_DOMF_privileged, &dom0->domain_flags);
- /* post-create hooks sets security label */
+ dom0->is_privileged = 1;
+
+ /* Post-create hook sets security label. */
acm_post_domain0_create(dom0->domain_id);
/* Grab the DOM0 command line. */
diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c
index 7b4dd77238..f9e92f1b51 100644
--- a/xen/arch/x86/traps.c
+++ b/xen/arch/x86/traps.c
@@ -134,7 +134,7 @@ static void show_guest_stack(struct cpu_user_regs *regs)
int i;
unsigned long *stack, addr;
- if ( hvm_guest(current) )
+ if ( is_hvm_vcpu(current) )
return;
if ( vm86_mode(regs) )
@@ -338,7 +338,6 @@ void show_execution_state(struct cpu_user_regs *regs)
*/
asmlinkage void fatal_trap(int trapnr, struct cpu_user_regs *regs)
{
- int cpu = smp_processor_id();
static char *trapstr[] = {
"divide error", "debug", "nmi", "bkpt", "overflow", "bounds",
"invalid opcode", "device not available", "double fault",
@@ -360,20 +359,10 @@ asmlinkage void fatal_trap(int trapnr, struct cpu_user_regs *regs)
show_page_walk(cr2);
}
- printk("************************************\n");
- printk("CPU%d FATAL TRAP %d (%s), ERROR_CODE %04x%s.\n",
- cpu, trapnr, trapstr[trapnr], regs->error_code,
- (regs->eflags & X86_EFLAGS_IF) ? "" : ", IN INTERRUPT CONTEXT");
- printk("System shutting down -- need manual reset.\n");
- printk("************************************\n");
-
- (void)debugger_trap_fatal(trapnr, regs);
-
- /* Lock up the console to prevent spurious output from other CPUs. */
- console_force_lock();
-
- /* Wait for manual reset. */
- machine_halt();
+ panic("FATAL TRAP: vector = %d (%s)\n"
+ "[error_code=%04x] %s\n",
+ trapnr, trapstr[trapnr], regs->error_code,
+ (regs->eflags & X86_EFLAGS_IF) ? "" : ", IN INTERRUPT CONTEXT");
}
static inline int do_trap(int trapnr, char *str,
@@ -407,7 +396,8 @@ static inline int do_trap(int trapnr, char *str,
if ( likely((fixup = search_exception_table(regs->eip)) != 0) )
{
- DPRINTK("Trap %d: %p -> %p\n", trapnr, _p(regs->eip), _p(fixup));
+ dprintk(XENLOG_ERR, "Trap %d: %p -> %p\n",
+ trapnr, _p(regs->eip), _p(fixup));
regs->eip = fixup;
return 0;
}
@@ -415,9 +405,9 @@ static inline int do_trap(int trapnr, char *str,
DEBUGGER_trap_fatal(trapnr, regs);
show_execution_state(regs);
- panic("CPU%d FATAL TRAP: vector = %d (%s)\n"
+ panic("FATAL TRAP: vector = %d (%s)\n"
"[error_code=%04x]\n",
- smp_processor_id(), trapnr, str, regs->error_code);
+ trapnr, str, regs->error_code);
return 0;
}
@@ -475,7 +465,8 @@ int wrmsr_hypervisor_regs(
if ( idx > 0 )
{
- DPRINTK("Dom%d: Out of range index %u to MSR %08x\n",
+ gdprintk(XENLOG_WARNING,
+ "Dom%d: Out of range index %u to MSR %08x\n",
d->domain_id, idx, 0x40000000);
return 0;
}
@@ -485,7 +476,8 @@ int wrmsr_hypervisor_regs(
if ( !mfn_valid(mfn) ||
!get_page_and_type(mfn_to_page(mfn), d, PGT_writable_page) )
{
- DPRINTK("Dom%d: Bad GMFN %lx (MFN %lx) to MSR %08x\n",
+ gdprintk(XENLOG_WARNING,
+ "Dom%d: Bad GMFN %lx (MFN %lx) to MSR %08x\n",
d->domain_id, gmfn, mfn, 0x40000000);
return 0;
}
@@ -627,8 +619,7 @@ asmlinkage int do_invalid_op(struct cpu_user_regs *regs)
}
DEBUGGER_trap_fatal(TRAP_invalid_op, regs);
show_execution_state(regs);
- panic("CPU%d FATAL TRAP: vector = %d (invalid opcode)\n",
- smp_processor_id(), TRAP_invalid_op);
+ panic("FATAL TRAP: vector = %d (invalid opcode)\n", TRAP_invalid_op);
}
if ( (rc = emulate_forced_invalid_op(regs)) != 0 )
@@ -656,7 +647,7 @@ asmlinkage int do_int3(struct cpu_user_regs *regs)
{
DEBUGGER_trap_fatal(TRAP_int3, regs);
show_execution_state(regs);
- panic("CPU%d FATAL TRAP: vector = 3 (Int3)\n", smp_processor_id());
+ panic("FATAL TRAP: vector = 3 (Int3)\n");
}
ti = &current->arch.guest_context.trap_ctxt[TRAP_int3];
@@ -701,12 +692,6 @@ void propagate_page_fault(unsigned long addr, u16 error_code)
static int handle_gdt_ldt_mapping_fault(
unsigned long offset, struct cpu_user_regs *regs)
{
- extern int map_ldt_shadow_page(unsigned int);
-
- struct vcpu *v = current;
- struct domain *d = v->domain;
- int ret;
-
/* Which vcpu's area did we fault in, and is it in the ldt sub-area? */
unsigned int is_ldt_area = (offset >> (GDT_LDT_VCPU_VA_SHIFT-1)) & 1;
unsigned int vcpu_area = (offset >> GDT_LDT_VCPU_VA_SHIFT);
@@ -720,18 +705,15 @@ static int handle_gdt_ldt_mapping_fault(
if ( likely(is_ldt_area) )
{
/* LDT fault: Copy a mapping from the guest's LDT, if it is valid. */
- LOCK_BIGLOCK(d);
- ret = map_ldt_shadow_page(offset >> PAGE_SHIFT);
- UNLOCK_BIGLOCK(d);
-
- if ( unlikely(ret == 0) )
+ if ( unlikely(map_ldt_shadow_page(offset >> PAGE_SHIFT) == 0) )
{
/* In hypervisor mode? Leave it to the #PF handler to fix up. */
if ( !guest_mode(regs) )
return 0;
/* In guest mode? Propagate #PF to guest, with adjusted %cr2. */
propagate_page_fault(
- v->arch.guest_context.ldt_base + offset, regs->error_code);
+ current->arch.guest_context.ldt_base + offset,
+ regs->error_code);
}
}
else
@@ -784,7 +766,7 @@ static int __spurious_page_fault(
#if CONFIG_PAGING_LEVELS >= 4
l4t = map_domain_page(mfn);
- l4e = l4t[l4_table_offset(addr)];
+ l4e = l4e_read_atomic(&l4t[l4_table_offset(addr)]);
mfn = l4e_get_pfn(l4e);
unmap_domain_page(l4t);
if ( ((l4e_get_flags(l4e) & required_flags) != required_flags) ||
@@ -797,7 +779,7 @@ static int __spurious_page_fault(
#ifdef CONFIG_X86_PAE
l3t += (cr3 & 0xFE0UL) >> 3;
#endif
- l3e = l3t[l3_table_offset(addr)];
+ l3e = l3e_read_atomic(&l3t[l3_table_offset(addr)]);
mfn = l3e_get_pfn(l3e);
unmap_domain_page(l3t);
#ifdef CONFIG_X86_PAE
@@ -811,7 +793,7 @@ static int __spurious_page_fault(
#endif
l2t = map_domain_page(mfn);
- l2e = l2t[l2_table_offset(addr)];
+ l2e = l2e_read_atomic(&l2t[l2_table_offset(addr)]);
mfn = l2e_get_pfn(l2e);
unmap_domain_page(l2t);
if ( ((l2e_get_flags(l2e) & required_flags) != required_flags) ||
@@ -824,7 +806,7 @@ static int __spurious_page_fault(
}
l1t = map_domain_page(mfn);
- l1e = l1t[l1_table_offset(addr)];
+ l1e = l1e_read_atomic(&l1t[l1_table_offset(addr)]);
mfn = l1e_get_pfn(l1e);
unmap_domain_page(l1t);
if ( ((l1e_get_flags(l1e) & required_flags) != required_flags) ||
@@ -832,17 +814,18 @@ static int __spurious_page_fault(
return 0;
spurious:
- DPRINTK("Spurious fault in domain %u:%u at addr %lx, e/c %04x\n",
+ dprintk(XENLOG_WARNING, "Spurious fault in domain %u:%u "
+ "at addr %lx, e/c %04x\n",
current->domain->domain_id, current->vcpu_id,
addr, regs->error_code);
#if CONFIG_PAGING_LEVELS >= 4
- DPRINTK(" l4e = %"PRIpte"\n", l4e_get_intpte(l4e));
+ dprintk(XENLOG_WARNING, " l4e = %"PRIpte"\n", l4e_get_intpte(l4e));
#endif
#if CONFIG_PAGING_LEVELS >= 3
- DPRINTK(" l3e = %"PRIpte"\n", l3e_get_intpte(l3e));
+ dprintk(XENLOG_WARNING, " l3e = %"PRIpte"\n", l3e_get_intpte(l3e));
#endif
- DPRINTK(" l2e = %"PRIpte"\n", l2e_get_intpte(l2e));
- DPRINTK(" l1e = %"PRIpte"\n", l1e_get_intpte(l1e));
+ dprintk(XENLOG_WARNING, " l2e = %"PRIpte"\n", l2e_get_intpte(l2e));
+ dprintk(XENLOG_WARNING, " l1e = %"PRIpte"\n", l1e_get_intpte(l1e));
#ifndef NDEBUG
show_registers(regs);
#endif
@@ -852,12 +835,16 @@ static int __spurious_page_fault(
static int spurious_page_fault(
unsigned long addr, struct cpu_user_regs *regs)
{
- struct domain *d = current->domain;
- int is_spurious;
+ unsigned long flags;
+ int is_spurious;
- LOCK_BIGLOCK(d);
+ /*
+ * Disabling interrupts prevents TLB flushing, and hence prevents
+ * page tables from becoming invalid under our feet during the walk.
+ */
+ local_irq_save(flags);
is_spurious = __spurious_page_fault(addr, regs);
- UNLOCK_BIGLOCK(d);
+ local_irq_restore(flags);
return is_spurious;
}
@@ -874,11 +861,7 @@ static int fixup_page_fault(unsigned long addr, struct cpu_user_regs *regs)
if ( (addr >= GDT_LDT_VIRT_START) && (addr < GDT_LDT_VIRT_END) )
return handle_gdt_ldt_mapping_fault(
addr - GDT_LDT_VIRT_START, regs);
- /*
- * Do not propagate spurious faults in the hypervisor area to the
- * guest. It cannot fix them up.
- */
- return (spurious_page_fault(addr, regs) ? EXCRET_not_a_fault : 0);
+ return 0;
}
if ( VM_ASSIST(d, VMASST_TYPE_writable_pagetables) &&
@@ -935,10 +918,10 @@ asmlinkage int do_page_fault(struct cpu_user_regs *regs)
show_execution_state(regs);
show_page_walk(addr);
- panic("CPU%d FATAL PAGE FAULT\n"
+ panic("FATAL PAGE FAULT\n"
"[error_code=%04x]\n"
"Faulting linear address: %p\n",
- smp_processor_id(), regs->error_code, _p(addr));
+ regs->error_code, _p(addr));
}
propagate_page_fault(addr, regs->error_code);
@@ -1313,7 +1296,8 @@ static int emulate_privileged_op(struct cpu_user_regs *regs)
case 0: /* Write CR0 */
if ( (*reg ^ read_cr0()) & ~X86_CR0_TS )
{
- DPRINTK("Attempt to change unmodifiable CR0 flags.\n");
+ gdprintk(XENLOG_WARNING,
+ "Attempt to change unmodifiable CR0 flags.\n");
goto fail;
}
(void)do_fpu_taskswitch(!!(*reg & X86_CR0_TS));
@@ -1333,7 +1317,7 @@ static int emulate_privileged_op(struct cpu_user_regs *regs)
case 4:
if ( *reg != (read_cr4() & ~(X86_CR4_PGE|X86_CR4_PSE)) )
{
- DPRINTK("Attempt to change CR4 flags.\n");
+ gdprintk(XENLOG_WARNING, "Attempt to change CR4 flags.\n");
goto fail;
}
break;
@@ -1381,7 +1365,7 @@ static int emulate_privileged_op(struct cpu_user_regs *regs)
if ( (rdmsr_safe(regs->ecx, l, h) != 0) ||
(regs->eax != l) || (regs->edx != h) )
- DPRINTK("Domain attempted WRMSR %p from "
+ gdprintk(XENLOG_WARNING, "Domain attempted WRMSR %p from "
"%08x:%08x to %08lx:%08lx.\n",
_p(regs->ecx), h, l, (long)regs->edx, (long)regs->eax);
break;
@@ -1417,7 +1401,8 @@ static int emulate_privileged_op(struct cpu_user_regs *regs)
break;
}
/* Everyone can read the MSR space. */
- /*DPRINTK("Domain attempted RDMSR %p.\n", _p(regs->ecx));*/
+ /* gdprintk(XENLOG_WARNING,"Domain attempted RDMSR %p.\n",
+ _p(regs->ecx));*/
if ( rdmsr_safe(regs->ecx, regs->eax, regs->edx) )
goto fail;
break;
@@ -1510,7 +1495,7 @@ asmlinkage int do_general_protection(struct cpu_user_regs *regs)
if ( likely((fixup = search_exception_table(regs->eip)) != 0) )
{
- DPRINTK("GPF (%04x): %p -> %p\n",
+ dprintk(XENLOG_WARNING, "GPF (%04x): %p -> %p\n",
regs->error_code, _p(regs->eip), _p(fixup));
regs->eip = fixup;
return 0;
@@ -1520,8 +1505,7 @@ asmlinkage int do_general_protection(struct cpu_user_regs *regs)
hardware_gp:
show_execution_state(regs);
- panic("CPU%d GENERAL PROTECTION FAULT\n[error_code=%04x]\n",
- smp_processor_id(), regs->error_code);
+ panic("GENERAL PROTECTION FAULT\n[error_code=%04x]\n", regs->error_code);
return 0;
}
diff --git a/xen/arch/x86/x86_32/asm-offsets.c b/xen/arch/x86/x86_32/asm-offsets.c
index 36197ef9e8..4153b9f967 100644
--- a/xen/arch/x86/x86_32/asm-offsets.c
+++ b/xen/arch/x86/x86_32/asm-offsets.c
@@ -86,6 +86,7 @@ void __dummy__(void)
BLANK();
OFFSET(VCPU_vmx_launched, struct vcpu, arch.hvm_vmx.launched);
+ OFFSET(VCPU_vmx_cr2, struct vcpu, arch.hvm_vmx.cpu_cr2);
BLANK();
OFFSET(VMCB_rax, struct vmcb_struct, rax);
diff --git a/xen/arch/x86/x86_32/domain_page.c b/xen/arch/x86/x86_32/domain_page.c
index b720003fc0..2b760ce704 100644
--- a/xen/arch/x86/x86_32/domain_page.c
+++ b/xen/arch/x86/x86_32/domain_page.c
@@ -29,7 +29,7 @@ static inline struct vcpu *mapcache_current_vcpu(void)
* then it means we are running on the idle domain's page table and must
* therefore use its mapcache.
*/
- if ( unlikely(!pagetable_get_pfn(v->arch.guest_table)) && !hvm_guest(v) )
+ if ( unlikely(!pagetable_get_pfn(v->arch.guest_table)) && !is_hvm_vcpu(v) )
{
/* If we really are idling, perform lazy context switch now. */
if ( (v = idle_vcpu[smp_processor_id()]) == current )
diff --git a/xen/arch/x86/x86_32/seg_fixup.c b/xen/arch/x86/x86_32/seg_fixup.c
index fd91ff14bd..643a1eec1f 100644
--- a/xen/arch/x86/x86_32/seg_fixup.c
+++ b/xen/arch/x86/x86_32/seg_fixup.c
@@ -32,10 +32,6 @@
#include <asm/regs.h>
#include <asm/x86_emulate.h>
-/* Make the scary benign errors go away. */
-#undef DPRINTK
-#define DPRINTK(_f, _a...) ((void)0)
-
/* General instruction properties. */
#define INSN_SUFFIX_BYTES (7)
#define OPCODE_BYTE (1<<4)
@@ -185,7 +181,7 @@ int fixup_seg(u16 seg, unsigned long offset)
table = (unsigned long *)LDT_VIRT_START(d);
if ( idx >= d->arch.guest_context.ldt_ents )
{
- DPRINTK("Segment %04x out of LDT range (%ld)\n",
+ dprintk(XENLOG_DEBUG, "Segment %04x out of LDT range (%ld)\n",
seg, d->arch.guest_context.ldt_ents);
goto fail;
}
@@ -195,7 +191,7 @@ int fixup_seg(u16 seg, unsigned long offset)
table = (unsigned long *)GDT_VIRT_START(d);
if ( idx >= d->arch.guest_context.gdt_ents )
{
- DPRINTK("Segment %04x out of GDT range (%ld)\n",
+ dprintk(XENLOG_DEBUG, "Segment %04x out of GDT range (%ld)\n",
seg, d->arch.guest_context.gdt_ents);
goto fail;
}
@@ -205,7 +201,7 @@ int fixup_seg(u16 seg, unsigned long offset)
if ( __get_user(a, &table[2*idx+0]) ||
__get_user(b, &table[2*idx+1]) )
{
- DPRINTK("Fault while reading segment %04x\n", seg);
+ dprintk(XENLOG_DEBUG, "Fault while reading segment %04x\n", seg);
goto fail; /* Barking up the wrong tree. Decode needs a page fault.*/
}
@@ -214,7 +210,7 @@ int fixup_seg(u16 seg, unsigned long offset)
_SEGMENT_G|_SEGMENT_CODE|_SEGMENT_DPL)) !=
(_SEGMENT_P|_SEGMENT_S|_SEGMENT_DB|_SEGMENT_G|_SEGMENT_DPL) )
{
- DPRINTK("Bad segment %08lx:%08lx\n", a, b);
+ dprintk(XENLOG_DEBUG, "Bad segment %08lx:%08lx\n", a, b);
goto fail;
}
@@ -244,7 +240,8 @@ int fixup_seg(u16 seg, unsigned long offset)
}
}
- DPRINTK("None of the above! (%08lx:%08lx, %08lx, %08lx, %08lx)\n",
+ dprintk(XENLOG_DEBUG, "None of the above! "
+ "(%08lx:%08lx, %08lx, %08lx, %08lx)\n",
a, b, base, limit, base+limit);
fail:
@@ -282,13 +279,14 @@ int gpf_emulate_4gb(struct cpu_user_regs *regs)
/* WARNING: We only work for ring-3 segments. */
if ( unlikely(vm86_mode(regs)) || unlikely(!ring_3(regs)) )
{
- DPRINTK("Taken fault at bad CS %04x\n", regs->cs);
+ dprintk(XENLOG_DEBUG, "Taken fault at bad CS %04x\n", regs->cs);
goto fail;
}
if ( !linearise_address((u16)regs->cs, regs->eip, (unsigned long *)&eip) )
{
- DPRINTK("Cannot linearise %04x:%08x\n", regs->cs, regs->eip);
+ dprintk(XENLOG_DEBUG, "Cannot linearise %04x:%08x\n",
+ regs->cs, regs->eip);
goto fail;
}
@@ -297,13 +295,16 @@ int gpf_emulate_4gb(struct cpu_user_regs *regs)
{
if ( get_user(b, pb) )
{
- DPRINTK("Fault while accessing byte %d of instruction\n", pb-eip);
+ dprintk(XENLOG_DEBUG,
+ "Fault while accessing byte %ld of instruction\n",
+ (long)(pb-eip));
goto page_fault;
}
if ( (pb - eip) >= 15 )
{
- DPRINTK("Too many instruction prefixes for a legal instruction\n");
+ dprintk(XENLOG_DEBUG, "Too many instruction prefixes for a "
+ "legal instruction\n");
goto fail;
}
@@ -315,7 +316,7 @@ int gpf_emulate_4gb(struct cpu_user_regs *regs)
case 0x26: /* ES override */
case 0x64: /* FS override */
case 0x36: /* SS override */
- DPRINTK("Unhandled prefix %02x\n", b);
+ dprintk(XENLOG_DEBUG, "Unhandled prefix %02x\n", b);
goto fail;
case 0x66: /* Operand-size override */
case 0xf0: /* LOCK */
@@ -333,7 +334,7 @@ int gpf_emulate_4gb(struct cpu_user_regs *regs)
if ( !gs_override )
{
- DPRINTK("Only instructions with GS override\n");
+ dprintk(XENLOG_DEBUG, "Only instructions with GS override\n");
goto fail;
}
@@ -341,7 +342,7 @@ int gpf_emulate_4gb(struct cpu_user_regs *regs)
pb++;
if ( decode == 0 )
{
- DPRINTK("Unsupported opcode %02x\n", b);
+ dprintk(XENLOG_DEBUG, "Unsupported opcode %02x\n", b);
goto fail;
}
@@ -353,7 +354,7 @@ int gpf_emulate_4gb(struct cpu_user_regs *regs)
if ( get_user(offset, (u32 *)pb) )
{
- DPRINTK("Fault while extracting <disp32>.\n");
+ dprintk(XENLOG_DEBUG, "Fault while extracting <disp32>.\n");
goto page_fault;
}
pb += 4;
@@ -367,7 +368,7 @@ int gpf_emulate_4gb(struct cpu_user_regs *regs)
if ( get_user(modrm, pb) )
{
- DPRINTK("Fault while extracting modrm byte\n");
+ dprintk(XENLOG_DEBUG, "Fault while extracting modrm byte\n");
goto page_fault;
}
@@ -379,7 +380,7 @@ int gpf_emulate_4gb(struct cpu_user_regs *regs)
if ( rm == 4 )
{
- DPRINTK("FIXME: Add decoding for the SIB byte.\n");
+ dprintk(XENLOG_DEBUG, "FIXME: Add decoding for the SIB byte.\n");
goto fixme;
}
@@ -397,7 +398,7 @@ int gpf_emulate_4gb(struct cpu_user_regs *regs)
memreg = NULL;
if ( get_user(disp32, (u32 *)pb) )
{
- DPRINTK("Fault while extracting <disp8>.\n");
+ dprintk(XENLOG_DEBUG, "Fault while extracting <disp8>.\n");
goto page_fault;
}
pb += 4;
@@ -407,7 +408,7 @@ int gpf_emulate_4gb(struct cpu_user_regs *regs)
case 1:
if ( get_user(disp8, pb) )
{
- DPRINTK("Fault while extracting <disp8>.\n");
+ dprintk(XENLOG_DEBUG, "Fault while extracting <disp8>.\n");
goto page_fault;
}
pb++;
@@ -417,14 +418,14 @@ int gpf_emulate_4gb(struct cpu_user_regs *regs)
case 2:
if ( get_user(disp32, (u32 *)pb) )
{
- DPRINTK("Fault while extracting <disp8>.\n");
+ dprintk(XENLOG_DEBUG, "Fault while extracting <disp8>.\n");
goto page_fault;
}
pb += 4;
break;
case 3:
- DPRINTK("Not a memory operand!\n");
+ dprintk(XENLOG_DEBUG, "Not a memory operand!\n");
goto fail;
}
@@ -455,7 +456,8 @@ int gpf_emulate_4gb(struct cpu_user_regs *regs)
return EXCRET_fault_fixed;
fixme:
- DPRINTK("Undecodable instruction %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x "
+ dprintk(XENLOG_DEBUG, "Undecodable instruction "
+ "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x "
"caused GPF(0) at %04x:%08x\n",
eip[0], eip[1], eip[2], eip[3],
eip[4], eip[5], eip[6], eip[7],
diff --git a/xen/arch/x86/x86_32/traps.c b/xen/arch/x86/x86_32/traps.c
index 90e799a78c..14a7c30d93 100644
--- a/xen/arch/x86/x86_32/traps.c
+++ b/xen/arch/x86/x86_32/traps.c
@@ -45,7 +45,7 @@ void show_registers(struct cpu_user_regs *regs)
unsigned long fault_crs[8];
const char *context;
- if ( hvm_guest(current) && guest_mode(regs) )
+ if ( is_hvm_vcpu(current) && guest_mode(regs) )
{
context = "hvm";
hvm_store_cpu_guest_regs(current, &fault_regs, fault_crs);
@@ -168,16 +168,8 @@ asmlinkage void do_double_fault(void)
printk("ds: %04x es: %04x fs: %04x gs: %04x ss: %04x\n",
tss->ds, tss->es, tss->fs, tss->gs, tss->ss);
show_stack_overflow(tss->esp);
- printk("************************************\n");
- printk("CPU%d DOUBLE FAULT -- system shutdown\n", cpu);
- printk("System needs manual reset.\n");
- printk("************************************\n");
- /* Lock up the console to prevent spurious output from other CPUs. */
- console_force_lock();
-
- /* Wait for manual reset. */
- machine_halt();
+ panic("DOUBLE FAULT -- system shutdown\n");
}
unsigned long do_iret(void)
@@ -515,7 +507,7 @@ static void hypercall_page_initialise_ring1_kernel(void *hypercall_page)
void hypercall_page_initialise(struct domain *d, void *hypercall_page)
{
- if ( hvm_guest(d->vcpu[0]) )
+ if ( is_hvm_domain(d) )
hvm_hypercall_page_initialise(d, hypercall_page);
else if ( supervisor_mode_kernel )
hypercall_page_initialise_ring0_kernel(hypercall_page);
diff --git a/xen/arch/x86/x86_64/asm-offsets.c b/xen/arch/x86/x86_64/asm-offsets.c
index 5e95815678..41f93fe9c5 100644
--- a/xen/arch/x86/x86_64/asm-offsets.c
+++ b/xen/arch/x86/x86_64/asm-offsets.c
@@ -80,6 +80,7 @@ void __dummy__(void)
BLANK();
OFFSET(VCPU_vmx_launched, struct vcpu, arch.hvm_vmx.launched);
+ OFFSET(VCPU_vmx_cr2, struct vcpu, arch.hvm_vmx.cpu_cr2);
BLANK();
OFFSET(VMCB_rax, struct vmcb_struct, rax);
diff --git a/xen/arch/x86/x86_64/traps.c b/xen/arch/x86/x86_64/traps.c
index e12c686441..a59cdaab70 100644
--- a/xen/arch/x86/x86_64/traps.c
+++ b/xen/arch/x86/x86_64/traps.c
@@ -42,7 +42,7 @@ void show_registers(struct cpu_user_regs *regs)
unsigned long fault_crs[8];
const char *context;
- if ( hvm_guest(current) && guest_mode(regs) )
+ if ( is_hvm_vcpu(current) && guest_mode(regs) )
{
context = "hvm";
hvm_store_cpu_guest_regs(current, &fault_regs, fault_crs);
@@ -172,16 +172,8 @@ asmlinkage void do_double_fault(struct cpu_user_regs *regs)
regs->r12, regs->r13, regs->r14);
printk("r15: %016lx\n", regs->r15);
show_stack_overflow(regs->rsp);
- printk("************************************\n");
- printk("CPU%d DOUBLE FAULT -- system shutdown\n", cpu);
- printk("System needs manual reset.\n");
- printk("************************************\n");
- /* Lock up the console to prevent spurious output from other CPUs. */
- console_force_lock();
-
- /* Wait for manual reset. */
- machine_halt();
+ panic("DOUBLE FAULT -- system shutdown\n");
}
void toggle_guest_mode(struct vcpu *v)
@@ -206,7 +198,8 @@ unsigned long do_iret(void)
if ( unlikely(copy_from_user(&iret_saved, (void *)regs->rsp,
sizeof(iret_saved))) )
{
- DPRINTK("Fault while reading IRET context from guest stack\n");
+ gdprintk(XENLOG_ERR, "Fault while reading IRET context from "
+ "guest stack\n");
domain_crash_synchronous();
}
@@ -215,7 +208,8 @@ unsigned long do_iret(void)
{
if ( unlikely(pagetable_is_null(v->arch.guest_table_user)) )
{
- DPRINTK("Guest switching to user mode with no user page tables\n");
+ gdprintk(XENLOG_ERR, "Guest switching to user mode with no "
+ "user page tables\n");
domain_crash_synchronous();
}
toggle_guest_mode(v);
@@ -227,7 +221,7 @@ unsigned long do_iret(void)
regs->rsp = iret_saved.rsp;
regs->ss = iret_saved.ss | 3; /* force guest privilege */
- if ( !(iret_saved.flags & VGCF_IN_SYSCALL) )
+ if ( !(iret_saved.flags & VGCF_in_syscall) )
{
regs->entry_vector = 0;
regs->r11 = iret_saved.r11;
@@ -498,7 +492,7 @@ static void hypercall_page_initialise_ring3_kernel(void *hypercall_page)
void hypercall_page_initialise(struct domain *d, void *hypercall_page)
{
- if ( hvm_guest(d->vcpu[0]) )
+ if ( is_hvm_domain(d) )
hvm_hypercall_page_initialise(d, hypercall_page);
else
hypercall_page_initialise_ring3_kernel(hypercall_page);
diff --git a/xen/arch/x86/x86_emulate.c b/xen/arch/x86/x86_emulate.c
index 4ce246b275..f189ac75f3 100644
--- a/xen/arch/x86/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate.c
@@ -10,14 +10,14 @@
#include <stdio.h>
#include <stdint.h>
#include <public/xen.h>
-#define DPRINTF(_f, _a...) printf( _f , ## _a )
+#define dprintf(_f, _a...) printf( _f , ## _a )
#else
#include <xen/config.h>
#include <xen/types.h>
#include <xen/lib.h>
#include <xen/mm.h>
#include <asm/regs.h>
-#define DPRINTF DPRINTK
+#define dprintf(_f, _a...) gdprintk(XENLOG_WARNING, _f , ## _a )
#endif
#include <asm-x86/x86_emulate.h>
@@ -560,7 +560,7 @@ x86_emulate_memop(
if ( modrm_mod == 3 )
{
- DPRINTF("Cannot parse ModRM.mod == 3.\n");
+ dprintf("Cannot parse ModRM.mod == 3.\n");
goto cannot_emulate;
}
@@ -970,7 +970,7 @@ x86_emulate_memop(
_regs.edi, (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes);
break;
case 0xa6 ... 0xa7: /* cmps */
- DPRINTF("Urk! I don't handle CMPS.\n");
+ dprintf("Urk! I don't handle CMPS.\n");
goto cannot_emulate;
case 0xaa ... 0xab: /* stos */
dst.type = OP_MEM;
@@ -990,7 +990,7 @@ x86_emulate_memop(
_regs.esi, (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes);
break;
case 0xae ... 0xaf: /* scas */
- DPRINTF("Urk! I don't handle SCAS.\n");
+ dprintf("Urk! I don't handle SCAS.\n");
goto cannot_emulate;
}
goto writeback;
@@ -1149,7 +1149,7 @@ x86_emulate_memop(
goto writeback;
cannot_emulate:
- DPRINTF("Cannot emulate %02x\n", b);
+ dprintf("Cannot emulate %02x\n", b);
return -1;
}
diff --git a/xen/common/domain.c b/xen/common/domain.c
index f96becc578..08327a37fd 100644
--- a/xen/common/domain.c
+++ b/xen/common/domain.c
@@ -35,6 +35,11 @@ struct domain *dom0;
struct vcpu *idle_vcpu[NR_CPUS] __read_mostly;
+int current_domain_id(void)
+{
+ return current->domain->domain_id;
+}
+
struct domain *alloc_domain(domid_t domid)
{
struct domain *d;
@@ -54,22 +59,24 @@ struct domain *alloc_domain(domid_t domid)
return d;
}
-
void free_domain(struct domain *d)
{
struct vcpu *v;
int i;
- sched_destroy_domain(d);
-
for ( i = MAX_VIRT_CPUS-1; i >= 0; i-- )
- if ( (v = d->vcpu[i]) != NULL )
- free_vcpu_struct(v);
+ {
+ if ( (v = d->vcpu[i]) == NULL )
+ continue;
+ vcpu_destroy(v);
+ sched_destroy_vcpu(v);
+ free_vcpu_struct(v);
+ }
+ sched_destroy_domain(d);
xfree(d);
}
-
struct vcpu *alloc_vcpu(
struct domain *d, unsigned int vcpu_id, unsigned int cpu_id)
{
@@ -77,7 +84,7 @@ struct vcpu *alloc_vcpu(
BUG_ON(d->vcpu[vcpu_id] != NULL);
- if ( (v = alloc_vcpu_struct(d, vcpu_id)) == NULL )
+ if ( (v = alloc_vcpu_struct()) == NULL )
return NULL;
v->domain = d;
@@ -91,12 +98,19 @@ struct vcpu *alloc_vcpu(
if ( (vcpu_id != 0) && !is_idle_domain(d) )
set_bit(_VCPUF_down, &v->vcpu_flags);
- if ( sched_init_vcpu(v, cpu_id) < 0 )
+ if ( sched_init_vcpu(v, cpu_id) != 0 )
{
free_vcpu_struct(v);
return NULL;
}
+ if ( vcpu_initialise(v) != 0 )
+ {
+ sched_destroy_vcpu(v);
+ free_vcpu_struct(v);
+ return NULL;
+ }
+
d->vcpu[vcpu_id] = v;
if ( vcpu_id != 0 )
d->vcpu[v->vcpu_id-1]->next_in_list = v;
@@ -111,7 +125,7 @@ struct vcpu *alloc_idle_vcpu(unsigned int cpu_id)
unsigned int vcpu_id = cpu_id % MAX_VIRT_CPUS;
d = (vcpu_id == 0) ?
- domain_create(IDLE_DOMAIN_ID) :
+ domain_create(IDLE_DOMAIN_ID, 0) :
idle_vcpu[cpu_id - vcpu_id]->domain;
BUG_ON(d == NULL);
@@ -121,13 +135,16 @@ struct vcpu *alloc_idle_vcpu(unsigned int cpu_id)
return v;
}
-struct domain *domain_create(domid_t domid)
+struct domain *domain_create(domid_t domid, unsigned int domcr_flags)
{
struct domain *d, **pd;
if ( (d = alloc_domain(domid)) == NULL )
return NULL;
+ if ( domcr_flags & DOMCRF_hvm )
+ d->is_hvm = 1;
+
rangeset_domain_initialise(d);
if ( !is_idle_domain(d) )
@@ -147,6 +164,9 @@ struct domain *domain_create(domid_t domid)
if ( (d->iomem_caps == NULL) || (d->irq_caps == NULL) )
goto fail4;
+ if ( sched_init_domain(d) != 0 )
+ goto fail4;
+
if ( !is_idle_domain(d) )
{
write_lock(&domlist_lock);
@@ -241,42 +261,6 @@ void __domain_crash_synchronous(void)
}
-static DEFINE_PER_CPU(struct domain *, domain_shuttingdown);
-
-static void domain_shutdown_finalise(void)
-{
- struct domain *d;
- struct vcpu *v;
-
- d = this_cpu(domain_shuttingdown);
- this_cpu(domain_shuttingdown) = NULL;
-
- BUG_ON(d == NULL);
- BUG_ON(d == current->domain);
-
- LOCK_BIGLOCK(d);
-
- /* Make sure that every vcpu is descheduled before we finalise. */
- for_each_vcpu ( d, v )
- vcpu_sleep_sync(v);
- BUG_ON(!cpus_empty(d->domain_dirty_cpumask));
-
- /* Don't set DOMF_shutdown until execution contexts are sync'ed. */
- if ( !test_and_set_bit(_DOMF_shutdown, &d->domain_flags) )
- send_guest_global_virq(dom0, VIRQ_DOM_EXC);
-
- UNLOCK_BIGLOCK(d);
-
- put_domain(d);
-}
-
-static __init int domain_shutdown_finaliser_init(void)
-{
- open_softirq(DOMAIN_SHUTDOWN_FINALISE_SOFTIRQ, domain_shutdown_finalise);
- return 0;
-}
-__initcall(domain_shutdown_finaliser_init);
-
void domain_shutdown(struct domain *d, u8 reason)
{
struct vcpu *v;
@@ -284,20 +268,13 @@ void domain_shutdown(struct domain *d, u8 reason)
if ( d->domain_id == 0 )
dom0_shutdown(reason);
- /* Mark the domain as shutting down. */
d->shutdown_code = reason;
+ set_bit(_DOMF_shutdown, &d->domain_flags);
- /* Put every vcpu to sleep, but don't wait (avoids inter-vcpu deadlock). */
- spin_lock(&d->pause_lock);
- d->pause_count++;
- set_bit(_DOMF_paused, &d->domain_flags);
- spin_unlock(&d->pause_lock);
for_each_vcpu ( d, v )
vcpu_sleep_nosync(v);
- get_knownalive_domain(d);
- this_cpu(domain_shuttingdown) = d;
- raise_softirq(DOMAIN_SHUTDOWN_FINALISE_SOFTIRQ);
+ send_guest_global_virq(dom0, VIRQ_DOM_EXC);
}
@@ -306,12 +283,8 @@ void domain_pause_for_debugger(void)
struct domain *d = current->domain;
struct vcpu *v;
- /*
- * NOTE: This does not synchronously pause the domain. The debugger
- * must issue a PAUSEDOMAIN command to ensure that all execution
- * has ceased and guest state is committed to memory.
- */
set_bit(_DOMF_ctrl_pause, &d->domain_flags);
+
for_each_vcpu ( d, v )
vcpu_sleep_nosync(v);
diff --git a/xen/common/domctl.c b/xen/common/domctl.c
index ecb77cd85d..bf6b8473aa 100644
--- a/xen/common/domctl.c
+++ b/xen/common/domctl.c
@@ -83,7 +83,7 @@ void getdomaininfo(struct domain *d, struct xen_domctl_getdomaininfo *info)
{
struct vcpu *v;
u64 cpu_time = 0;
- int flags = DOMFLAGS_BLOCKED;
+ int flags = XEN_DOMINF_blocked;
struct vcpu_runstate_info runstate;
info->domain = d->domain_id;
@@ -93,29 +93,33 @@ void getdomaininfo(struct domain *d, struct xen_domctl_getdomaininfo *info)
* - domain is marked as blocked only if all its vcpus are blocked
* - domain is marked as running if any of its vcpus is running
*/
- for_each_vcpu ( d, v ) {
+ for_each_vcpu ( d, v )
+ {
vcpu_runstate_get(v, &runstate);
cpu_time += runstate.time[RUNSTATE_running];
info->max_vcpu_id = v->vcpu_id;
if ( !test_bit(_VCPUF_down, &v->vcpu_flags) )
{
if ( !(v->vcpu_flags & VCPUF_blocked) )
- flags &= ~DOMFLAGS_BLOCKED;
+ flags &= ~XEN_DOMINF_blocked;
if ( v->vcpu_flags & VCPUF_running )
- flags |= DOMFLAGS_RUNNING;
+ flags |= XEN_DOMINF_running;
info->nr_online_vcpus++;
}
}
-
+
info->cpu_time = cpu_time;
-
+
info->flags = flags |
- ((d->domain_flags & DOMF_dying) ? DOMFLAGS_DYING : 0) |
- ((d->domain_flags & DOMF_shutdown) ? DOMFLAGS_SHUTDOWN : 0) |
- ((d->domain_flags & DOMF_ctrl_pause) ? DOMFLAGS_PAUSED : 0) |
- d->shutdown_code << DOMFLAGS_SHUTDOWNSHIFT;
+ ((d->domain_flags & DOMF_dying) ? XEN_DOMINF_dying : 0) |
+ ((d->domain_flags & DOMF_shutdown) ? XEN_DOMINF_shutdown : 0) |
+ ((d->domain_flags & DOMF_ctrl_pause) ? XEN_DOMINF_paused : 0) |
+ d->shutdown_code << XEN_DOMINF_shutdownshift;
+
+ if ( is_hvm_domain(d) )
+ info->flags |= XEN_DOMINF_hvm_guest;
- if (d->ssid != NULL)
+ if ( d->ssid != NULL )
info->ssidref = ((struct acm_ssid_domain *)d->ssid)->ssidref;
else
info->ssidref = ACM_DEFAULT_SSID;
@@ -241,12 +245,10 @@ long do_domctl(XEN_GUEST_HANDLE(xen_domctl_t) u_domctl)
struct domain *d;
domid_t dom;
static domid_t rover = 0;
+ unsigned int domcr_flags;
- /*
- * Running the domain 0 kernel in ring 0 is not compatible
- * with multiple guests.
- */
- if ( supervisor_mode_kernel )
+ if ( supervisor_mode_kernel ||
+ (op->u.createdomain.flags & ~XEN_DOMCTL_CDF_hvm_guest) )
return -EINVAL;
dom = op->domain;
@@ -273,8 +275,12 @@ long do_domctl(XEN_GUEST_HANDLE(xen_domctl_t) u_domctl)
rover = dom;
}
+ domcr_flags = 0;
+ if ( op->u.createdomain.flags & XEN_DOMCTL_CDF_hvm_guest )
+ domcr_flags |= DOMCRF_hvm;
+
ret = -ENOMEM;
- if ( (d = domain_create(dom)) == NULL )
+ if ( (d = domain_create(dom, domcr_flags)) == NULL )
break;
memcpy(d->handle, op->u.createdomain.handle,
diff --git a/xen/common/event_channel.c b/xen/common/event_channel.c
index e333144cf2..b0d23eb8e0 100644
--- a/xen/common/event_channel.c
+++ b/xen/common/event_channel.c
@@ -40,7 +40,8 @@
#define ERROR_EXIT(_errno) \
do { \
- DPRINTK("EVTCHNOP failure: domain %d, error %d, line %d\n", \
+ gdprintk(XENLOG_WARNING, \
+ "EVTCHNOP failure: domain %d, error %d, line %d\n", \
current->domain->domain_id, (_errno), __LINE__); \
rc = (_errno); \
goto out; \
diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c
index 0e98628bd2..3b6ad11548 100644
--- a/xen/common/grant_table.c
+++ b/xen/common/grant_table.c
@@ -47,7 +47,7 @@ union grant_combo {
#define PIN_FAIL(_lbl, _rc, _f, _a...) \
do { \
- DPRINTK( _f, ## _a ); \
+ gdprintk(XENLOG_WARNING, _f, ## _a ); \
rc = (_rc); \
goto _lbl; \
} while ( 0 )
@@ -109,7 +109,8 @@ __gnttab_map_grant_ref(
if ( unlikely(op->ref >= NR_GRANT_ENTRIES) ||
unlikely((op->flags & (GNTMAP_device_map|GNTMAP_host_map)) == 0) )
{
- DPRINTK("Bad ref (%d) or flags (%x).\n", op->ref, op->flags);
+ gdprintk(XENLOG_INFO, "Bad ref (%d) or flags (%x).\n",
+ op->ref, op->flags);
op->status = GNTST_bad_gntref;
return;
}
@@ -124,7 +125,7 @@ __gnttab_map_grant_ref(
{
if ( rd != NULL )
put_domain(rd);
- DPRINTK("Could not find domain %d\n", op->dom);
+ gdprintk(XENLOG_INFO, "Could not find domain %d\n", op->dom);
op->status = GNTST_bad_domain;
return;
}
@@ -139,7 +140,7 @@ __gnttab_map_grant_ref(
if ( (lgt->maptrack_limit << 1) > MAPTRACK_MAX_ENTRIES )
{
put_domain(rd);
- DPRINTK("Maptrack table is at maximum size.\n");
+ gdprintk(XENLOG_INFO, "Maptrack table is at maximum size.\n");
op->status = GNTST_no_device_space;
return;
}
@@ -149,7 +150,7 @@ __gnttab_map_grant_ref(
if ( new_mt == NULL )
{
put_domain(rd);
- DPRINTK("No more map handles available.\n");
+ gdprintk(XENLOG_INFO, "No more map handles available.\n");
op->status = GNTST_no_device_space;
return;
}
@@ -166,7 +167,7 @@ __gnttab_map_grant_ref(
lgt->maptrack_order += 1;
lgt->maptrack_limit <<= 1;
- DPRINTK("Doubled maptrack size\n");
+ gdprintk(XENLOG_INFO, "Doubled maptrack size\n");
handle = get_maptrack_handle(ld->grant_table);
}
@@ -252,8 +253,12 @@ __gnttab_map_grant_ref(
get_page(mfn_to_page(frame), rd) :
get_page_and_type(mfn_to_page(frame), rd,
PGT_writable_page))) )
- PIN_FAIL(undo_out, GNTST_general_error,
- "Could not pin the granted frame (%lx)!\n", frame);
+ {
+ if ( !test_bit(_DOMF_dying, &rd->domain_flags) )
+ gdprintk(XENLOG_WARNING, "Could not pin grant frame %lx\n", frame);
+ rc = GNTST_general_error;
+ goto undo_out;
+ }
if ( op->flags & GNTMAP_host_map )
{
@@ -353,7 +358,7 @@ __gnttab_unmap_grant_ref(
if ( unlikely(op->handle >= ld->grant_table->maptrack_limit) ||
unlikely(!map->flags) )
{
- DPRINTK("Bad handle (%d).\n", op->handle);
+ gdprintk(XENLOG_INFO, "Bad handle (%d).\n", op->handle);
op->status = GNTST_bad_handle;
return;
}
@@ -366,7 +371,7 @@ __gnttab_unmap_grant_ref(
{
if ( rd != NULL )
put_domain(rd);
- DPRINTK("Could not find domain %d\n", dom);
+ gdprintk(XENLOG_INFO, "Could not find domain %d\n", dom);
op->status = GNTST_bad_domain;
return;
}
@@ -486,13 +491,14 @@ gnttab_setup_table(
if ( unlikely(copy_from_guest(&op, uop, 1) != 0) )
{
- DPRINTK("Fault while reading gnttab_setup_table_t.\n");
+ gdprintk(XENLOG_INFO, "Fault while reading gnttab_setup_table_t.\n");
return -EFAULT;
}
if ( unlikely(op.nr_frames > NR_GRANT_FRAMES) )
{
- DPRINTK("Xen only supports up to %d grant-table frames per domain.\n",
+ gdprintk(XENLOG_INFO, "Xen only supports up to %d grant-table frames"
+ " per domain.\n",
NR_GRANT_FRAMES);
op.status = GNTST_general_error;
goto out;
@@ -511,7 +517,7 @@ gnttab_setup_table(
if ( unlikely((d = find_domain_by_id(dom)) == NULL) )
{
- DPRINTK("Bad domid %d.\n", dom);
+ gdprintk(XENLOG_INFO, "Bad domid %d.\n", dom);
op.status = GNTST_bad_domain;
goto out;
}
@@ -549,7 +555,7 @@ gnttab_prepare_for_transfer(
if ( unlikely((rgt = rd->grant_table) == NULL) ||
unlikely(ref >= NR_GRANT_ENTRIES) )
{
- DPRINTK("Dom %d has no g.t., or ref is bad (%d).\n",
+ gdprintk(XENLOG_INFO, "Dom %d has no g.t., or ref is bad (%d).\n",
rd->domain_id, ref);
return 0;
}
@@ -565,7 +571,8 @@ gnttab_prepare_for_transfer(
if ( unlikely(scombo.shorts.flags != GTF_accept_transfer) ||
unlikely(scombo.shorts.domid != ld->domain_id) )
{
- DPRINTK("Bad flags (%x) or dom (%d). (NB. expected dom %d)\n",
+ gdprintk(XENLOG_INFO, "Bad flags (%x) or dom (%d). "
+ "(NB. expected dom %d)\n",
scombo.shorts.flags, scombo.shorts.domid,
ld->domain_id);
goto fail;
@@ -581,7 +588,7 @@ gnttab_prepare_for_transfer(
if ( retries++ == 4 )
{
- DPRINTK("Shared grant entry is unstable.\n");
+ gdprintk(XENLOG_WARNING, "Shared grant entry is unstable.\n");
goto fail;
}
@@ -613,7 +620,8 @@ gnttab_transfer(
/* Read from caller address space. */
if ( unlikely(__copy_from_guest_offset(&gop, uop, i, 1)) )
{
- DPRINTK("gnttab_transfer: error reading req %d/%d\n", i, count);
+ gdprintk(XENLOG_INFO, "gnttab_transfer: error reading req %d/%d\n",
+ i, count);
return -EFAULT;
}
@@ -622,7 +630,7 @@ gnttab_transfer(
/* Check the passed page frame for basic validity. */
if ( unlikely(!mfn_valid(mfn)) )
{
- DPRINTK("gnttab_transfer: out-of-range %lx\n",
+ gdprintk(XENLOG_INFO, "gnttab_transfer: out-of-range %lx\n",
(unsigned long)gop.mfn);
gop.status = GNTST_bad_page;
goto copyback;
@@ -631,7 +639,7 @@ gnttab_transfer(
page = mfn_to_page(mfn);
if ( unlikely(IS_XEN_HEAP_FRAME(page)) )
{
- DPRINTK("gnttab_transfer: xen frame %lx\n",
+ gdprintk(XENLOG_INFO, "gnttab_transfer: xen frame %lx\n",
(unsigned long)gop.mfn);
gop.status = GNTST_bad_page;
goto copyback;
@@ -646,7 +654,8 @@ gnttab_transfer(
/* Find the target domain. */
if ( unlikely((e = find_domain_by_id(gop.domid)) == NULL) )
{
- DPRINTK("gnttab_transfer: can't find domain %d\n", gop.domid);
+ gdprintk(XENLOG_INFO, "gnttab_transfer: can't find domain %d\n",
+ gop.domid);
page->count_info &= ~(PGC_count_mask|PGC_allocated);
free_domheap_page(page);
gop.status = GNTST_bad_domain;
@@ -665,7 +674,8 @@ gnttab_transfer(
unlikely(!gnttab_prepare_for_transfer(e, d, gop.ref)) )
{
if ( !test_bit(_DOMF_dying, &e->domain_flags) )
- DPRINTK("gnttab_transfer: Transferee has no reservation "
+ gdprintk(XENLOG_INFO, "gnttab_transfer: "
+ "Transferee has no reservation "
"headroom (%d,%d) or provided a bad grant ref (%08x) "
"or is dying (%lx)\n",
e->tot_pages, e->max_pages, gop.ref, e->domain_flags);
@@ -701,7 +711,8 @@ gnttab_transfer(
copyback:
if ( unlikely(__copy_to_guest_offset(uop, i, &gop, 1)) )
{
- DPRINTK("gnttab_transfer: error writing resp %d/%d\n", i, count);
+ gdprintk(XENLOG_INFO, "gnttab_transfer: error writing resp %d/%d\n",
+ i, count);
return -EFAULT;
}
}
@@ -717,10 +728,6 @@ __release_grant_for_copy(
{
grant_entry_t *const sha = &rd->grant_table->shared[gref];
struct active_grant_entry *const act = &rd->grant_table->active[gref];
- const unsigned long r_frame = act->frame;
-
- if ( !readonly )
- gnttab_mark_dirty(rd, r_frame);
spin_lock(&rd->grant_table->lock);
@@ -743,7 +750,8 @@ __release_grant_for_copy(
/* Grab a frame number from a grant entry and update the flags and pin
count as appropriate. Note that this does *not* update the page
- type or reference counts. */
+ type or reference counts, and does not check that the mfn is
+ actually valid. */
static int
__acquire_grant_for_copy(
struct domain *rd, unsigned long gref, int readonly,
@@ -885,9 +893,16 @@ __gnttab_copy(
{
s_frame = gmfn_to_mfn(sd, op->source.u.gmfn);
}
- if ( !get_page(mfn_to_page(s_frame), sd) )
+ if ( unlikely(!mfn_valid(s_frame)) )
PIN_FAIL(error_out, GNTST_general_error,
- "could not get source frame %lx.\n", s_frame);
+ "source frame %lx invalid.\n", s_frame);
+ if ( !get_page(mfn_to_page(s_frame), sd) )
+ {
+ if ( !test_bit(_DOMF_dying, &sd->domain_flags) )
+ gdprintk(XENLOG_WARNING, "Could not get src frame %lx\n", s_frame);
+ rc = GNTST_general_error;
+ goto error_out;
+ }
have_s_ref = 1;
if ( dest_is_gref )
@@ -899,11 +914,18 @@ __gnttab_copy(
}
else
{
- d_frame = gmfn_to_mfn(sd, op->dest.u.gmfn);
+ d_frame = gmfn_to_mfn(dd, op->dest.u.gmfn);
}
- if ( !get_page_and_type(mfn_to_page(d_frame), dd, PGT_writable_page) )
+ if ( unlikely(!mfn_valid(d_frame)) )
PIN_FAIL(error_out, GNTST_general_error,
- "could not get destination frame %lx.\n", d_frame);
+ "destination frame %lx invalid.\n", d_frame);
+ if ( !get_page_and_type(mfn_to_page(d_frame), dd, PGT_writable_page) )
+ {
+ if ( !test_bit(_DOMF_dying, &dd->domain_flags) )
+ gdprintk(XENLOG_WARNING, "Could not get dst frame %lx\n", d_frame);
+ rc = GNTST_general_error;
+ goto error_out;
+ }
sp = map_domain_page(s_frame);
dp = map_domain_page(d_frame);
@@ -913,6 +935,8 @@ __gnttab_copy(
unmap_domain_page(dp);
unmap_domain_page(sp);
+ gnttab_mark_dirty(dd, d_frame);
+
put_page_and_type(mfn_to_page(d_frame));
error_out:
if ( have_s_ref )
@@ -1090,7 +1114,8 @@ gnttab_release_mappings(
ref = map->ref;
- DPRINTK("Grant release (%hu) ref:(%hu) flags:(%x) dom:(%hu)\n",
+ gdprintk(XENLOG_INFO, "Grant release (%hu) ref:(%hu) "
+ "flags:(%x) dom:(%hu)\n",
handle, ref, map->flags, map->domid);
rd = find_domain_by_id(map->domid);
diff --git a/xen/common/keyhandler.c b/xen/common/keyhandler.c
index 1486bdf7b5..85fe6d89ca 100644
--- a/xen/common/keyhandler.c
+++ b/xen/common/keyhandler.c
@@ -36,8 +36,10 @@ static void keypress_softirq(void)
{
keyhandler_t *h;
unsigned char key = keypress_key;
+ console_start_log_everything();
if ( (h = key_table[key].u.handler) != NULL )
(*h)(key);
+ console_end_log_everything();
}
void handle_keypress(unsigned char key, struct cpu_user_regs *regs)
@@ -46,8 +48,10 @@ void handle_keypress(unsigned char key, struct cpu_user_regs *regs)
if ( key_table[key].flags & KEYHANDLER_IRQ_CALLBACK )
{
+ console_start_log_everything();
if ( (h = key_table[key].u.irq_handler) != NULL )
(*h)(key, regs);
+ console_end_log_everything();
}
else
{
diff --git a/xen/common/lib.c b/xen/common/lib.c
index 408e8eefe6..0eb58f19d2 100644
--- a/xen/common/lib.c
+++ b/xen/common/lib.c
@@ -439,21 +439,28 @@ s64 __moddi3(s64 a, s64 b)
#endif /* BITS_PER_LONG == 32 */
-unsigned long long parse_size_and_unit(char *s)
+unsigned long long parse_size_and_unit(const char *s, char **ps)
{
- unsigned long long ret = simple_strtoull(s, &s, 0);
+ unsigned long long ret = simple_strtoull(s, (char **)&s, 0);
switch (*s) {
case 'G': case 'g':
ret <<= 10;
case 'M': case 'm':
ret <<= 10;
- case 'K': case 'k': default:
+ case 'K': case 'k':
ret <<= 10;
case 'B': case 'b':
+ s++;
+ break;
+ default:
+ ret <<= 10; /* default to kB */
break;
}
+ if (ps != NULL)
+ *ps = (char *)s;
+
return ret;
}
diff --git a/xen/common/memory.c b/xen/common/memory.c
index 9ab62a48ec..33ead8e259 100644
--- a/xen/common/memory.c
+++ b/xen/common/memory.c
@@ -29,98 +29,105 @@
*/
#define START_EXTENT_SHIFT 4 /* cmd[:4] == start_extent */
-static long
-increase_reservation(
- struct domain *d,
- XEN_GUEST_HANDLE(xen_pfn_t) extent_list,
- unsigned int nr_extents,
- unsigned int extent_order,
- unsigned int memflags,
- int *preempted)
+struct memop_args {
+ /* INPUT */
+ struct domain *domain; /* Domain to be affected. */
+ XEN_GUEST_HANDLE(xen_pfn_t) extent_list; /* List of extent base addrs. */
+ unsigned int nr_extents; /* Number of extents to allocate or free. */
+ unsigned int extent_order; /* Size of each extent. */
+ unsigned int memflags; /* Allocation flags. */
+
+ /* INPUT/OUTPUT */
+ unsigned int nr_done; /* Number of extents processed so far. */
+ int preempted; /* Was the hypercall preempted? */
+};
+
+static unsigned int select_local_cpu(struct domain *d)
+{
+ struct vcpu *v = d->vcpu[0];
+ return (v ? v->processor : 0);
+}
+
+static void increase_reservation(struct memop_args *a)
{
struct page_info *page;
unsigned long i;
xen_pfn_t mfn;
- /* use domain's first processor for locality parameter */
- unsigned int cpu = d->vcpu[0]->processor;
+ struct domain *d = a->domain;
+ unsigned int cpu = select_local_cpu(d);
- if ( !guest_handle_is_null(extent_list) &&
- !guest_handle_okay(extent_list, nr_extents) )
- return 0;
+ if ( !guest_handle_is_null(a->extent_list) &&
+ !guest_handle_okay(a->extent_list, a->nr_extents) )
+ return;
- if ( (extent_order != 0) &&
+ if ( (a->extent_order != 0) &&
!multipage_allocation_permitted(current->domain) )
- return 0;
+ return;
- for ( i = 0; i < nr_extents; i++ )
+ for ( i = a->nr_done; i < a->nr_extents; i++ )
{
if ( hypercall_preempt_check() )
{
- *preempted = 1;
- return i;
+ a->preempted = 1;
+ goto out;
}
- if ( unlikely((page = __alloc_domheap_pages( d, cpu,
- extent_order, memflags )) == NULL) )
+ page = __alloc_domheap_pages(d, cpu, a->extent_order, a->memflags);
+ if ( unlikely(page == NULL) )
{
- DPRINTK("Could not allocate order=%d extent: "
+ gdprintk(XENLOG_INFO, "Could not allocate order=%d extent: "
"id=%d memflags=%x (%ld of %d)\n",
- extent_order, d->domain_id, memflags, i, nr_extents);
- return i;
+ a->extent_order, d->domain_id, a->memflags,
+ i, a->nr_extents);
+ goto out;
}
/* Inform the domain of the new page's machine address. */
- if ( !guest_handle_is_null(extent_list) )
+ if ( !guest_handle_is_null(a->extent_list) )
{
mfn = page_to_mfn(page);
- if ( unlikely(__copy_to_guest_offset(extent_list, i, &mfn, 1)) )
- return i;
+ if ( unlikely(__copy_to_guest_offset(a->extent_list, i, &mfn, 1)) )
+ goto out;
}
}
- return nr_extents;
+ out:
+ a->nr_done = i;
}
-static long
-populate_physmap(
- struct domain *d,
- XEN_GUEST_HANDLE(xen_pfn_t) extent_list,
- unsigned int nr_extents,
- unsigned int extent_order,
- unsigned int memflags,
- int *preempted)
+static void populate_physmap(struct memop_args *a)
{
struct page_info *page;
unsigned long i, j;
- xen_pfn_t gpfn;
- xen_pfn_t mfn;
- /* use domain's first processor for locality parameter */
- unsigned int cpu = d->vcpu[0]->processor;
+ xen_pfn_t gpfn, mfn;
+ struct domain *d = a->domain;
+ unsigned int cpu = select_local_cpu(d);
- if ( !guest_handle_okay(extent_list, nr_extents) )
- return 0;
+ if ( !guest_handle_okay(a->extent_list, a->nr_extents) )
+ return;
- if ( (extent_order != 0) &&
+ if ( (a->extent_order != 0) &&
!multipage_allocation_permitted(current->domain) )
- return 0;
+ return;
- for ( i = 0; i < nr_extents; i++ )
+ for ( i = a->nr_done; i < a->nr_extents; i++ )
{
if ( hypercall_preempt_check() )
{
- *preempted = 1;
+ a->preempted = 1;
goto out;
}
- if ( unlikely(__copy_from_guest_offset(&gpfn, extent_list, i, 1)) )
+ if ( unlikely(__copy_from_guest_offset(&gpfn, a->extent_list, i, 1)) )
goto out;
- if ( unlikely((page = __alloc_domheap_pages( d, cpu,
- extent_order, memflags )) == NULL) )
+ page = __alloc_domheap_pages(d, cpu, a->extent_order, a->memflags);
+ if ( unlikely(page == NULL) )
{
- DPRINTK("Could not allocate order=%d extent: "
- "id=%d memflags=%x (%ld of %d)\n",
- extent_order, d->domain_id, memflags, i, nr_extents);
+ gdprintk(XENLOG_INFO, "Could not allocate order=%d extent: "
+ "id=%d memflags=%x (%ld of %d)\n",
+ a->extent_order, d->domain_id, a->memflags,
+ i, a->nr_extents);
goto out;
}
@@ -128,28 +135,25 @@ populate_physmap(
if ( unlikely(shadow_mode_translate(d)) )
{
- for ( j = 0; j < (1 << extent_order); j++ )
+ for ( j = 0; j < (1 << a->extent_order); j++ )
guest_physmap_add_page(d, gpfn + j, mfn + j);
}
else
{
- for ( j = 0; j < (1 << extent_order); j++ )
+ for ( j = 0; j < (1 << a->extent_order); j++ )
set_gpfn_from_mfn(mfn + j, gpfn + j);
/* Inform the domain of the new page's machine address. */
- if ( unlikely(__copy_to_guest_offset(extent_list, i, &mfn, 1)) )
+ if ( unlikely(__copy_to_guest_offset(a->extent_list, i, &mfn, 1)) )
goto out;
}
}
out:
- return i;
+ a->nr_done = i;
}
-int
-guest_remove_page(
- struct domain *d,
- unsigned long gmfn)
+int guest_remove_page(struct domain *d, unsigned long gmfn)
{
struct page_info *page;
unsigned long mfn;
@@ -157,7 +161,7 @@ guest_remove_page(
mfn = gmfn_to_mfn(d, gmfn);
if ( unlikely(!mfn_valid(mfn)) )
{
- DPRINTK("Domain %u page number %lx invalid\n",
+ gdprintk(XENLOG_INFO, "Domain %u page number %lx invalid\n",
d->domain_id, gmfn);
return 0;
}
@@ -165,7 +169,7 @@ guest_remove_page(
page = mfn_to_page(mfn);
if ( unlikely(!get_page(page, d)) )
{
- DPRINTK("Bad page free for domain %u\n", d->domain_id);
+ gdprintk(XENLOG_INFO, "Bad page free for domain %u\n", d->domain_id);
return 0;
}
@@ -178,7 +182,7 @@ guest_remove_page(
if ( unlikely(!page_is_removable(page)) )
{
/* We'll make this a guest-visible error in future, so take heed! */
- DPRINTK("Dom%d freeing in-use page %lx (pseudophys %lx):"
+ gdprintk(XENLOG_INFO, "Dom%d freeing in-use page %lx (pseudophys %lx):"
" count=%lx type=%lx\n",
d->domain_id, mfn, get_gpfn_from_mfn(mfn),
(unsigned long)page->count_info, page->u.inuse.type_info);
@@ -191,43 +195,35 @@ guest_remove_page(
return 1;
}
-static long
-decrease_reservation(
- struct domain *d,
- XEN_GUEST_HANDLE(xen_pfn_t) extent_list,
- unsigned int nr_extents,
- unsigned int extent_order,
- int *preempted)
+static void decrease_reservation(struct memop_args *a)
{
unsigned long i, j;
xen_pfn_t gmfn;
- if ( !guest_handle_okay(extent_list, nr_extents) )
- return 0;
+ if ( !guest_handle_okay(a->extent_list, a->nr_extents) )
+ return;
- for ( i = 0; i < nr_extents; i++ )
+ for ( i = a->nr_done; i < a->nr_extents; i++ )
{
if ( hypercall_preempt_check() )
{
- *preempted = 1;
- return i;
+ a->preempted = 1;
+ goto out;
}
- if ( unlikely(__copy_from_guest_offset(&gmfn, extent_list, i, 1)) )
- return i;
+ if ( unlikely(__copy_from_guest_offset(&gmfn, a->extent_list, i, 1)) )
+ goto out;
- for ( j = 0; j < (1 << extent_order); j++ )
- {
- if ( !guest_remove_page(d, gmfn + j) )
- return i;
- }
+ for ( j = 0; j < (1 << a->extent_order); j++ )
+ if ( !guest_remove_page(a->domain, gmfn + j) )
+ goto out;
}
- return nr_extents;
+ out:
+ a->nr_done = i;
}
-static long
-translate_gpfn_list(
+static long translate_gpfn_list(
XEN_GUEST_HANDLE(xen_translate_gpfn_list_t) uop, unsigned long *progress)
{
struct xen_translate_gpfn_list op;
@@ -289,8 +285,7 @@ translate_gpfn_list(
return 0;
}
-static long
-memory_exchange(XEN_GUEST_HANDLE(xen_memory_exchange_t) arg)
+static long memory_exchange(XEN_GUEST_HANDLE(xen_memory_exchange_t) arg)
{
struct xen_memory_exchange exch;
LIST_HEAD(in_chunk_list);
@@ -341,24 +336,15 @@ memory_exchange(XEN_GUEST_HANDLE(xen_memory_exchange_t) arg)
memflags = MEMF_dma;
}
- guest_handle_add_offset(exch.in.extent_start, exch.nr_exchanged);
- exch.in.nr_extents -= exch.nr_exchanged;
-
if ( exch.in.extent_order <= exch.out.extent_order )
{
in_chunk_order = exch.out.extent_order - exch.in.extent_order;
out_chunk_order = 0;
- guest_handle_add_offset(
- exch.out.extent_start, exch.nr_exchanged >> in_chunk_order);
- exch.out.nr_extents -= exch.nr_exchanged >> in_chunk_order;
}
else
{
in_chunk_order = 0;
out_chunk_order = exch.in.extent_order - exch.out.extent_order;
- guest_handle_add_offset(
- exch.out.extent_start, exch.nr_exchanged << out_chunk_order);
- exch.out.nr_extents -= exch.nr_exchanged << out_chunk_order;
}
/*
@@ -372,14 +358,15 @@ memory_exchange(XEN_GUEST_HANDLE(xen_memory_exchange_t) arg)
}
d = current->domain;
- /* use domain's first processor for locality parameter */
- cpu = d->vcpu[0]->processor;
+ cpu = select_local_cpu(d);
- for ( i = 0; i < (exch.in.nr_extents >> in_chunk_order); i++ )
+ for ( i = (exch.nr_exchanged >> in_chunk_order);
+ i < (exch.in.nr_extents >> in_chunk_order);
+ i++ )
{
if ( hypercall_preempt_check() )
{
- exch.nr_exchanged += i << in_chunk_order;
+ exch.nr_exchanged = i << in_chunk_order;
if ( copy_field_to_guest(arg, &exch, nr_exchanged) )
return -EFAULT;
return hypercall_create_continuation(
@@ -420,8 +407,8 @@ memory_exchange(XEN_GUEST_HANDLE(xen_memory_exchange_t) arg)
/* Allocate a chunk's worth of anonymous output pages. */
for ( j = 0; j < (1UL << out_chunk_order); j++ )
{
- page = __alloc_domheap_pages( NULL, cpu,
- exch.out.extent_order, memflags);
+ page = __alloc_domheap_pages(
+ NULL, cpu, exch.out.extent_order, memflags);
if ( unlikely(page == NULL) )
{
rc = -ENOMEM;
@@ -480,7 +467,7 @@ memory_exchange(XEN_GUEST_HANDLE(xen_memory_exchange_t) arg)
BUG_ON(j != (1UL << out_chunk_order));
}
- exch.nr_exchanged += exch.in.nr_extents;
+ exch.nr_exchanged = exch.in.nr_extents;
if ( copy_field_to_guest(arg, &exch, nr_exchanged) )
rc = -EFAULT;
return rc;
@@ -507,7 +494,7 @@ memory_exchange(XEN_GUEST_HANDLE(xen_memory_exchange_t) arg)
free_domheap_pages(page, exch.out.extent_order);
}
- exch.nr_exchanged += i << in_chunk_order;
+ exch.nr_exchanged = i << in_chunk_order;
fail_early:
if ( copy_field_to_guest(arg, &exch, nr_exchanged) )
@@ -518,10 +505,10 @@ memory_exchange(XEN_GUEST_HANDLE(xen_memory_exchange_t) arg)
long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE(void) arg)
{
struct domain *d;
- int rc, op, preempted = 0;
- unsigned int memflags = 0;
+ int rc, op;
unsigned long start_extent, progress;
struct xen_memory_reservation reservation;
+ struct memop_args args;
domid_t domid;
op = cmd & ((1 << START_EXTENT_SHIFT) - 1);
@@ -543,9 +530,12 @@ long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE(void) arg)
if ( unlikely(start_extent > reservation.nr_extents) )
return start_extent;
- if ( !guest_handle_is_null(reservation.extent_start) )
- guest_handle_add_offset(reservation.extent_start, start_extent);
- reservation.nr_extents -= start_extent;
+ args.extent_list = reservation.extent_start;
+ args.nr_extents = reservation.nr_extents;
+ args.extent_order = reservation.extent_order;
+ args.nr_done = start_extent;
+ args.preempted = 0;
+ args.memflags = 0;
if ( (reservation.address_bits != 0) &&
(reservation.address_bits <
@@ -553,7 +543,7 @@ long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE(void) arg)
{
if ( reservation.address_bits < 31 )
return start_extent;
- memflags = MEMF_dma;
+ args.memflags = MEMF_dma;
}
if ( likely(reservation.domid == DOMID_SELF) )
@@ -561,44 +551,27 @@ long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE(void) arg)
else if ( !IS_PRIV(current->domain) ||
((d = find_domain_by_id(reservation.domid)) == NULL) )
return start_extent;
+ args.domain = d;
switch ( op )
{
case XENMEM_increase_reservation:
- rc = increase_reservation(
- d,
- reservation.extent_start,
- reservation.nr_extents,
- reservation.extent_order,
- memflags,
- &preempted);
+ increase_reservation(&args);
break;
case XENMEM_decrease_reservation:
- rc = decrease_reservation(
- d,
- reservation.extent_start,
- reservation.nr_extents,
- reservation.extent_order,
- &preempted);
+ decrease_reservation(&args);
break;
- case XENMEM_populate_physmap:
- default:
- rc = populate_physmap(
- d,
- reservation.extent_start,
- reservation.nr_extents,
- reservation.extent_order,
- memflags,
- &preempted);
+ default: /* XENMEM_populate_physmap */
+ populate_physmap(&args);
break;
}
if ( unlikely(reservation.domid != DOMID_SELF) )
put_domain(d);
- rc += start_extent;
+ rc = args.nr_done;
- if ( preempted )
+ if ( args.preempted )
return hypercall_create_continuation(
__HYPERVISOR_memory_op, "lh",
op | (rc << START_EXTENT_SHIFT), arg);
diff --git a/xen/common/multicall.c b/xen/common/multicall.c
index 7ba43ebd64..499e6bd62c 100644
--- a/xen/common/multicall.c
+++ b/xen/common/multicall.c
@@ -24,7 +24,7 @@ do_multicall(
if ( unlikely(__test_and_set_bit(_MCSF_in_multicall, &mcs->flags)) )
{
- DPRINTK("Multicall reentry is disallowed.\n");
+ gdprintk(XENLOG_INFO, "Multicall reentry is disallowed.\n");
return -EINVAL;
}
diff --git a/xen/common/page_alloc.c b/xen/common/page_alloc.c
index f4a1adc274..93fc193e17 100644
--- a/xen/common/page_alloc.c
+++ b/xen/common/page_alloc.c
@@ -54,7 +54,7 @@ static unsigned long lowmem_emergency_pool_pages;
static void parse_lowmem_emergency_pool(char *s)
{
unsigned long long bytes;
- bytes = parse_size_and_unit(s);
+ bytes = parse_size_and_unit(s, NULL);
lowmem_emergency_pool_pages = bytes >> PAGE_SHIFT;
}
custom_param("lowmem_emergency_pool", parse_lowmem_emergency_pool);
@@ -599,7 +599,8 @@ int assign_pages(
if ( unlikely(test_bit(_DOMF_dying, &d->domain_flags)) )
{
- DPRINTK("Cannot assign page to domain%d -- dying.\n", d->domain_id);
+ gdprintk(XENLOG_INFO, "Cannot assign page to domain%d -- dying.\n",
+ d->domain_id);
goto fail;
}
@@ -607,7 +608,7 @@ int assign_pages(
{
if ( unlikely((d->tot_pages + (1 << order)) > d->max_pages) )
{
- DPRINTK("Over-allocation for domain %u: %u > %u\n",
+ gdprintk(XENLOG_INFO, "Over-allocation for domain %u: %u > %u\n",
d->domain_id, d->tot_pages + (1 << order), d->max_pages);
goto fail;
}
diff --git a/xen/common/perfc.c b/xen/common/perfc.c
index 1903ef2f4e..471bd3cd2b 100644
--- a/xen/common/perfc.c
+++ b/xen/common/perfc.c
@@ -143,9 +143,6 @@ static int perfc_copy_info(XEN_GUEST_HANDLE(xen_sysctl_perfc_desc_t) desc,
unsigned int v = 0;
atomic_t *counters = (atomic_t *)&perfcounters;
- if ( guest_handle_is_null(desc) )
- return 0;
-
/* We only copy the name and array-size information once. */
if ( !perfc_init )
{
@@ -175,7 +172,11 @@ static int perfc_copy_info(XEN_GUEST_HANDLE(xen_sysctl_perfc_desc_t) desc,
perfc_vals = xmalloc_array(xen_sysctl_perfc_val_t, perfc_nbr_vals);
perfc_init = 1;
}
- if (perfc_vals == NULL)
+
+ if ( guest_handle_is_null(desc) )
+ return 0;
+
+ if ( perfc_vals == NULL )
return -ENOMEM;
/* Architecture may fill counters from hardware. */
@@ -207,9 +208,9 @@ static int perfc_copy_info(XEN_GUEST_HANDLE(xen_sysctl_perfc_desc_t) desc,
}
BUG_ON(v != perfc_nbr_vals);
- if (copy_to_guest(desc, (xen_sysctl_perfc_desc_t *)perfc_d, NR_PERFCTRS))
+ if ( copy_to_guest(desc, (xen_sysctl_perfc_desc_t *)perfc_d, NR_PERFCTRS) )
return -EFAULT;
- if (copy_to_guest(val, perfc_vals, perfc_nbr_vals))
+ if ( copy_to_guest(val, perfc_vals, perfc_nbr_vals) )
return -EFAULT;
return 0;
}
diff --git a/xen/common/sched_credit.c b/xen/common/sched_credit.c
index 1dd54bbf52..f83f03146b 100644
--- a/xen/common/sched_credit.c
+++ b/xen/common/sched_credit.c
@@ -36,16 +36,23 @@
/*
* Basic constants
*/
-#define CSCHED_TICK 10 /* milliseconds */
-#define CSCHED_TSLICE 30 /* milliseconds */
-#define CSCHED_ACCT_NTICKS 3
-#define CSCHED_ACCT_PERIOD (CSCHED_ACCT_NTICKS * CSCHED_TICK)
-#define CSCHED_DEFAULT_WEIGHT 256
+#define CSCHED_DEFAULT_WEIGHT 256
+#define CSCHED_TICKS_PER_TSLICE 3
+#define CSCHED_TICKS_PER_ACCT 3
+#define CSCHED_MSECS_PER_TICK 10
+#define CSCHED_MSECS_PER_TSLICE \
+ (CSCHED_MSECS_PER_TICK * CSCHED_TICKS_PER_TSLICE)
+#define CSCHED_CREDITS_PER_TICK 100
+#define CSCHED_CREDITS_PER_TSLICE \
+ (CSCHED_CREDITS_PER_TICK * CSCHED_TICKS_PER_TSLICE)
+#define CSCHED_CREDITS_PER_ACCT \
+ (CSCHED_CREDITS_PER_TICK * CSCHED_TICKS_PER_ACCT)
/*
* Priorities
*/
+#define CSCHED_PRI_TS_BOOST 0 /* time-share waking up */
#define CSCHED_PRI_TS_UNDER -1 /* time-share w/ credits */
#define CSCHED_PRI_TS_OVER -2 /* time-share w/o credits */
#define CSCHED_PRI_IDLE -64 /* idle */
@@ -75,40 +82,48 @@
printk("\t%-30s = %u\n", #_X, CSCHED_STAT(_X)); \
} while ( 0 );
+/*
+ * Try and keep often cranked stats on top so they'll fit on one
+ * cache line.
+ */
#define CSCHED_STATS_EXPAND_SCHED(_MACRO) \
- _MACRO(vcpu_init) \
+ _MACRO(schedule) \
+ _MACRO(acct_run) \
+ _MACRO(acct_no_work) \
+ _MACRO(acct_balance) \
+ _MACRO(acct_reorder) \
+ _MACRO(acct_min_credit) \
+ _MACRO(acct_vcpu_active) \
+ _MACRO(acct_vcpu_idle) \
_MACRO(vcpu_sleep) \
_MACRO(vcpu_wake_running) \
_MACRO(vcpu_wake_onrunq) \
_MACRO(vcpu_wake_runnable) \
_MACRO(vcpu_wake_not_runnable) \
- _MACRO(dom_destroy) \
- _MACRO(schedule) \
_MACRO(tickle_local_idler) \
_MACRO(tickle_local_over) \
_MACRO(tickle_local_under) \
_MACRO(tickle_local_other) \
- _MACRO(acct_run) \
- _MACRO(acct_no_work) \
- _MACRO(acct_balance) \
- _MACRO(acct_reorder) \
- _MACRO(acct_min_credit) \
- _MACRO(acct_vcpu_active) \
- _MACRO(acct_vcpu_idle) \
- _MACRO(acct_vcpu_credit_min)
-
-#define CSCHED_STATS_EXPAND_SMP_LOAD_BALANCE(_MACRO) \
- _MACRO(vcpu_migrate) \
- _MACRO(load_balance_idle) \
- _MACRO(load_balance_over) \
- _MACRO(load_balance_other) \
- _MACRO(steal_trylock_failed) \
- _MACRO(steal_peer_down) \
- _MACRO(steal_peer_idle) \
- _MACRO(steal_peer_running) \
- _MACRO(steal_peer_pinned) \
- _MACRO(tickle_idlers_none) \
- _MACRO(tickle_idlers_some)
+ _MACRO(tickle_idlers_none) \
+ _MACRO(tickle_idlers_some) \
+ _MACRO(vcpu_migrate) \
+ _MACRO(load_balance_idle) \
+ _MACRO(load_balance_over) \
+ _MACRO(load_balance_other) \
+ _MACRO(steal_trylock_failed) \
+ _MACRO(steal_peer_down) \
+ _MACRO(steal_peer_idle) \
+ _MACRO(steal_peer_running) \
+ _MACRO(steal_peer_pinned) \
+ _MACRO(steal_peer_migrating) \
+ _MACRO(steal_peer_best_idler) \
+ _MACRO(steal_loner_candidate) \
+ _MACRO(steal_loner_signal) \
+ _MACRO(cpu_pick) \
+ _MACRO(dom_init) \
+ _MACRO(dom_destroy) \
+ _MACRO(vcpu_init) \
+ _MACRO(vcpu_destroy)
#ifndef NDEBUG
#define CSCHED_STATS_EXPAND_CHECKS(_MACRO) \
@@ -117,10 +132,9 @@
#define CSCHED_STATS_EXPAND_CHECKS(_MACRO)
#endif
-#define CSCHED_STATS_EXPAND(_MACRO) \
- CSCHED_STATS_EXPAND_SCHED(_MACRO) \
- CSCHED_STATS_EXPAND_SMP_LOAD_BALANCE(_MACRO) \
- CSCHED_STATS_EXPAND_CHECKS(_MACRO)
+#define CSCHED_STATS_EXPAND(_MACRO) \
+ CSCHED_STATS_EXPAND_CHECKS(_MACRO) \
+ CSCHED_STATS_EXPAND_SCHED(_MACRO)
#define CSCHED_STATS_RESET() \
do \
@@ -170,11 +184,14 @@ struct csched_vcpu {
struct csched_dom *sdom;
struct vcpu *vcpu;
atomic_t credit;
- int credit_last;
- uint32_t credit_incr;
- uint32_t state_active;
- uint32_t state_idle;
int16_t pri;
+ struct {
+ int credit_last;
+ uint32_t credit_incr;
+ uint32_t state_active;
+ uint32_t state_idle;
+ uint32_t migrate;
+ } stats;
};
/*
@@ -313,7 +330,7 @@ csched_pcpu_init(int cpu)
spin_lock_irqsave(&csched_priv.lock, flags);
/* Initialize/update system-wide config */
- csched_priv.credit += CSCHED_ACCT_PERIOD;
+ csched_priv.credit += CSCHED_CREDITS_PER_ACCT;
if ( csched_priv.ncpus <= cpu )
csched_priv.ncpus = cpu + 1;
if ( csched_priv.master >= csched_priv.ncpus )
@@ -358,8 +375,42 @@ __csched_vcpu_check(struct vcpu *vc)
#define CSCHED_VCPU_CHECK(_vc)
#endif
+/*
+ * Indicates which of two given idlers is most efficient to run
+ * an additional VCPU.
+ *
+ * Returns:
+ * 0: They are the same.
+ * negative: One is less efficient than Two.
+ * positive: One is more efficient than Two.
+ */
+static int
+csched_idler_compare(int one, int two)
+{
+ cpumask_t idlers;
+ cpumask_t one_idlers;
+ cpumask_t two_idlers;
+
+ idlers = csched_priv.idlers;
+ cpu_clear(one, idlers);
+ cpu_clear(two, idlers);
+
+ if ( cpu_isset(one, cpu_core_map[two]) )
+ {
+ cpus_and(one_idlers, idlers, cpu_sibling_map[one]);
+ cpus_and(two_idlers, idlers, cpu_sibling_map[two]);
+ }
+ else
+ {
+ cpus_and(one_idlers, idlers, cpu_core_map[one]);
+ cpus_and(two_idlers, idlers, cpu_core_map[two]);
+ }
+
+ return cpus_weight(one_idlers) - cpus_weight(two_idlers);
+}
+
static inline int
-__csched_vcpu_is_stealable(int local_cpu, struct vcpu *vc)
+__csched_queued_vcpu_is_stealable(int local_cpu, struct vcpu *vc)
{
/*
* Don't pick up work that's in the peer's scheduling tail. Also only pick
@@ -380,6 +431,32 @@ __csched_vcpu_is_stealable(int local_cpu, struct vcpu *vc)
return 1;
}
+static inline int
+__csched_running_vcpu_is_stealable(int local_cpu, struct vcpu *vc)
+{
+ BUG_ON( is_idle_vcpu(vc) );
+
+ if ( unlikely(!cpu_isset(local_cpu, vc->cpu_affinity)) )
+ {
+ CSCHED_STAT_CRANK(steal_peer_pinned);
+ return 0;
+ }
+
+ if ( test_bit(_VCPUF_migrating, &vc->vcpu_flags) )
+ {
+ CSCHED_STAT_CRANK(steal_peer_migrating);
+ return 0;
+ }
+
+ if ( csched_idler_compare(local_cpu, vc->processor) <= 0 )
+ {
+ CSCHED_STAT_CRANK(steal_peer_best_idler);
+ return 0;
+ }
+
+ return 1;
+}
+
static void
csched_vcpu_acct(struct csched_vcpu *svc, int credit_dec)
{
@@ -397,7 +474,7 @@ csched_vcpu_acct(struct csched_vcpu *svc, int credit_dec)
if ( list_empty(&svc->active_vcpu_elem) )
{
CSCHED_STAT_CRANK(acct_vcpu_active);
- svc->state_active++;
+ svc->stats.state_active++;
sdom->active_vcpu_count++;
list_add(&svc->active_vcpu_elem, &sdom->active_vcpu);
@@ -410,6 +487,14 @@ csched_vcpu_acct(struct csched_vcpu *svc, int credit_dec)
spin_unlock_irqrestore(&csched_priv.lock, flags);
}
+
+ /*
+ * If this VCPU's priority was boosted when it last awoke, reset it.
+ * If the VCPU is found here, then it's consuming a non-negligeable
+ * amount of CPU resources and should no longer be boosted.
+ */
+ if ( svc->pri == CSCHED_PRI_TS_BOOST )
+ svc->pri = CSCHED_PRI_TS_UNDER;
}
static inline void
@@ -420,7 +505,7 @@ __csched_vcpu_acct_idle_locked(struct csched_vcpu *svc)
BUG_ON( list_empty(&svc->active_vcpu_elem) );
CSCHED_STAT_CRANK(acct_vcpu_idle);
- svc->state_idle++;
+ svc->stats.state_idle++;
sdom->active_vcpu_count--;
list_del_init(&svc->active_vcpu_elem);
@@ -430,51 +515,20 @@ __csched_vcpu_acct_idle_locked(struct csched_vcpu *svc)
list_del_init(&sdom->active_sdom_elem);
csched_priv.weight -= sdom->weight;
}
-
- atomic_set(&svc->credit, 0);
}
static int
csched_vcpu_init(struct vcpu *vc)
{
struct domain * const dom = vc->domain;
- struct csched_dom *sdom;
+ struct csched_dom *sdom = CSCHED_DOM(dom);
struct csched_vcpu *svc;
- int16_t pri;
CSCHED_STAT_CRANK(vcpu_init);
- /* Allocate, if appropriate, per-domain info */
- if ( is_idle_vcpu(vc) )
- {
- sdom = NULL;
- pri = CSCHED_PRI_IDLE;
- }
- else if ( CSCHED_DOM(dom) )
- {
- sdom = CSCHED_DOM(dom);
- pri = CSCHED_PRI_TS_UNDER;
- }
- else
- {
- sdom = xmalloc(struct csched_dom);
- if ( !sdom )
- return -1;
-
- /* Initialize credit and weight */
- INIT_LIST_HEAD(&sdom->active_vcpu);
- sdom->active_vcpu_count = 0;
- INIT_LIST_HEAD(&sdom->active_sdom_elem);
- sdom->dom = dom;
- sdom->weight = CSCHED_DEFAULT_WEIGHT;
- sdom->cap = 0U;
- dom->sched_priv = sdom;
- pri = CSCHED_PRI_TS_UNDER;
- }
-
/* Allocate per-VCPU info */
svc = xmalloc(struct csched_vcpu);
- if ( !svc )
+ if ( svc == NULL )
return -1;
INIT_LIST_HEAD(&svc->runq_elem);
@@ -482,11 +536,8 @@ csched_vcpu_init(struct vcpu *vc)
svc->sdom = sdom;
svc->vcpu = vc;
atomic_set(&svc->credit, 0);
- svc->credit_last = 0;
- svc->credit_incr = 0U;
- svc->state_active = 0U;
- svc->state_idle = 0U;
- svc->pri = pri;
+ svc->pri = is_idle_domain(dom) ? CSCHED_PRI_IDLE : CSCHED_PRI_TS_UNDER;
+ memset(&svc->stats, 0, sizeof(svc->stats));
vc->sched_priv = svc;
CSCHED_VCPU_CHECK(vc);
@@ -508,12 +559,14 @@ csched_vcpu_init(struct vcpu *vc)
}
static void
-csched_vcpu_free(struct vcpu *vc)
+csched_vcpu_destroy(struct vcpu *vc)
{
struct csched_vcpu * const svc = CSCHED_VCPU(vc);
struct csched_dom * const sdom = svc->sdom;
unsigned long flags;
+ CSCHED_STAT_CRANK(vcpu_destroy);
+
BUG_ON( sdom == NULL );
BUG_ON( !list_empty(&svc->runq_elem) );
@@ -566,6 +619,25 @@ csched_vcpu_wake(struct vcpu *vc)
else
CSCHED_STAT_CRANK(vcpu_wake_not_runnable);
+ /*
+ * We temporarly boost the priority of awaking VCPUs!
+ *
+ * If this VCPU consumes a non negligeable amount of CPU, it
+ * will eventually find itself in the credit accounting code
+ * path where its priority will be reset to normal.
+ *
+ * If on the other hand the VCPU consumes little CPU and is
+ * blocking and awoken a lot (doing I/O for example), its
+ * priority will remain boosted, optimizing it's wake-to-run
+ * latencies.
+ *
+ * This allows wake-to-run latency sensitive VCPUs to preempt
+ * more CPU resource intensive VCPUs without impacting overall
+ * system fairness.
+ */
+ if ( svc->pri == CSCHED_PRI_TS_UNDER )
+ svc->pri = CSCHED_PRI_TS_BOOST;
+
/* Put the VCPU on the runq and tickle CPUs */
__runq_insert(cpu, svc);
__runq_tickle(cpu, svc);
@@ -609,21 +681,100 @@ csched_dom_cntl(
return 0;
}
+static int
+csched_dom_init(struct domain *dom)
+{
+ struct csched_dom *sdom;
+
+ CSCHED_STAT_CRANK(dom_init);
+
+ if ( is_idle_domain(dom) )
+ return 0;
+
+ sdom = xmalloc(struct csched_dom);
+ if ( sdom == NULL )
+ return -ENOMEM;
+
+ /* Initialize credit and weight */
+ INIT_LIST_HEAD(&sdom->active_vcpu);
+ sdom->active_vcpu_count = 0;
+ INIT_LIST_HEAD(&sdom->active_sdom_elem);
+ sdom->dom = dom;
+ sdom->weight = CSCHED_DEFAULT_WEIGHT;
+ sdom->cap = 0U;
+ dom->sched_priv = sdom;
+
+ return 0;
+}
+
static void
csched_dom_destroy(struct domain *dom)
{
struct csched_dom * const sdom = CSCHED_DOM(dom);
- int i;
CSCHED_STAT_CRANK(dom_destroy);
- for ( i = 0; i < MAX_VIRT_CPUS; i++ )
+ xfree(sdom);
+}
+
+static int
+csched_cpu_pick(struct vcpu *vc)
+{
+ cpumask_t cpus;
+ int cpu, nxt;
+
+ CSCHED_STAT_CRANK(cpu_pick);
+
+ /*
+ * Pick from online CPUs in VCPU's affinity mask, giving a
+ * preference to its current processor if it's in there.
+ */
+ cpus_and(cpus, cpu_online_map, vc->cpu_affinity);
+ ASSERT( !cpus_empty(cpus) );
+ cpu = cpu_isset(vc->processor, cpus) ? vc->processor : first_cpu(cpus);
+
+ /*
+ * Try to find an idle processor within the above constraints.
+ */
+ cpus_and(cpus, cpus, csched_priv.idlers);
+ if ( !cpus_empty(cpus) )
{
- if ( dom->vcpu[i] )
- csched_vcpu_free(dom->vcpu[i]);
+ cpu = cpu_isset(cpu, cpus) ? cpu : first_cpu(cpus);
+ cpu_clear(cpu, cpus);
+
+ /*
+ * In multi-core and multi-threaded CPUs, not all idle execution
+ * vehicles are equal!
+ *
+ * We give preference to the idle execution vehicle with the most
+ * idling neighbours in its grouping. This distributes work across
+ * distinct cores first and guarantees we don't do something stupid
+ * like run two VCPUs on co-hyperthreads while there are idle cores
+ * or sockets.
+ */
+ while ( !cpus_empty(cpus) )
+ {
+ nxt = first_cpu(cpus);
+
+ if ( csched_idler_compare(cpu, nxt) < 0 )
+ {
+ cpu = nxt;
+ cpu_clear(nxt, cpus);
+ }
+ else if ( cpu_isset(cpu, cpu_core_map[nxt]) )
+ {
+ cpus_andnot(cpus, cpus, cpu_sibling_map[nxt]);
+ }
+ else
+ {
+ cpus_andnot(cpus, cpus, cpu_core_map[nxt]);
+ }
+
+ ASSERT( !cpu_isset(nxt, cpus) );
+ }
}
- xfree(sdom);
+ return cpu;
}
/*
@@ -659,7 +810,7 @@ csched_runq_sort(unsigned int cpu)
next = elem->next;
svc_elem = __runq_elem(elem);
- if ( svc_elem->pri == CSCHED_PRI_TS_UNDER )
+ if ( svc_elem->pri >= CSCHED_PRI_TS_UNDER )
{
/* does elem need to move up the runq? */
if ( elem->prev != last_under )
@@ -689,6 +840,7 @@ csched_acct(void)
uint32_t weight_left;
uint32_t credit_fair;
uint32_t credit_peak;
+ uint32_t credit_cap;
int credit_balance;
int credit_xtra;
int credit;
@@ -719,6 +871,7 @@ csched_acct(void)
weight_left = weight_total;
credit_balance = 0;
credit_xtra = 0;
+ credit_cap = 0U;
list_for_each_safe( iter_sdom, next_sdom, &csched_priv.active_sdom )
{
@@ -739,18 +892,22 @@ csched_acct(void)
* for one full accounting period. We allow a domain to earn more
* only when the system-wide credit balance is negative.
*/
- credit_peak = sdom->active_vcpu_count * CSCHED_ACCT_PERIOD;
+ credit_peak = sdom->active_vcpu_count * CSCHED_CREDITS_PER_ACCT;
if ( csched_priv.credit_balance < 0 )
{
credit_peak += ( ( -csched_priv.credit_balance * sdom->weight) +
(weight_total - 1)
) / weight_total;
}
+
if ( sdom->cap != 0U )
{
- uint32_t credit_cap = ((sdom->cap * CSCHED_ACCT_PERIOD) + 99) / 100;
+ credit_cap = ((sdom->cap * CSCHED_CREDITS_PER_ACCT) + 99) / 100;
if ( credit_cap < credit_peak )
credit_peak = credit_cap;
+
+ credit_cap = ( credit_cap + ( sdom->active_vcpu_count - 1 )
+ ) / sdom->active_vcpu_count;
}
credit_fair = ( ( credit_total * sdom->weight) + (weight_total - 1)
@@ -806,15 +963,15 @@ csched_acct(void)
*/
if ( credit < 0 )
{
- if ( sdom->cap == 0U )
- svc->pri = CSCHED_PRI_TS_OVER;
- else
+ if ( sdom->cap != 0U && credit < -credit_cap )
svc->pri = CSCHED_PRI_TS_PARKED;
+ else
+ svc->pri = CSCHED_PRI_TS_OVER;
- if ( credit < -CSCHED_TSLICE )
+ if ( credit < -CSCHED_CREDITS_PER_TSLICE )
{
CSCHED_STAT_CRANK(acct_min_credit);
- credit = -CSCHED_TSLICE;
+ credit = -CSCHED_CREDITS_PER_TSLICE;
atomic_set(&svc->credit, credit);
}
}
@@ -822,12 +979,16 @@ csched_acct(void)
{
svc->pri = CSCHED_PRI_TS_UNDER;
- if ( credit > CSCHED_TSLICE )
+ if ( credit > CSCHED_CREDITS_PER_TSLICE )
+ {
__csched_vcpu_acct_idle_locked(svc);
+ credit = 0;
+ atomic_set(&svc->credit, credit);
+ }
}
- svc->credit_last = credit;
- svc->credit_incr = credit_fair;
+ svc->stats.credit_last = credit;
+ svc->stats.credit_incr = credit_fair;
credit_balance += credit;
}
}
@@ -853,7 +1014,7 @@ csched_tick(unsigned int cpu)
*/
if ( likely(sdom != NULL) )
{
- csched_vcpu_acct(svc, CSCHED_TICK);
+ csched_vcpu_acct(svc, CSCHED_CREDITS_PER_TICK);
}
/*
@@ -863,7 +1024,7 @@ csched_tick(unsigned int cpu)
* we could distribute or at the very least cycle the duty.
*/
if ( (csched_priv.master == cpu) &&
- (per_cpu(schedule_data, cpu).tick % CSCHED_ACCT_NTICKS) == 0 )
+ (per_cpu(schedule_data, cpu).tick % CSCHED_TICKS_PER_ACCT) == 0 )
{
csched_acct();
}
@@ -903,7 +1064,7 @@ csched_runq_steal(struct csched_pcpu *spc, int cpu, int pri)
vc = speer->vcpu;
BUG_ON( is_idle_vcpu(vc) );
- if ( __csched_vcpu_is_stealable(cpu, vc) )
+ if ( __csched_queued_vcpu_is_stealable(cpu, vc) )
{
/* We got a candidate. Grab it! */
__runq_remove(speer);
@@ -919,8 +1080,11 @@ csched_runq_steal(struct csched_pcpu *spc, int cpu, int pri)
static struct csched_vcpu *
csched_load_balance(int cpu, struct csched_vcpu *snext)
{
- struct csched_pcpu *spc;
struct csched_vcpu *speer;
+ struct csched_pcpu *spc;
+ struct vcpu *peer_vcpu;
+ cpumask_t workers;
+ cpumask_t loners;
int peer_cpu;
if ( snext->pri == CSCHED_PRI_IDLE )
@@ -930,15 +1094,24 @@ csched_load_balance(int cpu, struct csched_vcpu *snext)
else
CSCHED_STAT_CRANK(load_balance_other);
+ /*
+ * Peek at non-idling CPUs in the system
+ */
+ cpus_clear(loners);
+ cpus_andnot(workers, cpu_online_map, csched_priv.idlers);
+ cpu_clear(cpu, workers);
+
peer_cpu = cpu;
BUG_ON( peer_cpu != snext->vcpu->processor );
- while ( 1 )
+ while ( !cpus_empty(workers) )
{
- /* For each PCPU in the system starting with our neighbour... */
- peer_cpu = (peer_cpu + 1) % csched_priv.ncpus;
- if ( peer_cpu == cpu )
- break;
+ /* For each CPU of interest, starting with our neighbour... */
+ peer_cpu = next_cpu(peer_cpu, workers);
+ if ( peer_cpu == NR_CPUS )
+ peer_cpu = first_cpu(workers);
+
+ cpu_clear(peer_cpu, workers);
/*
* Get ahold of the scheduler lock for this peer CPU.
@@ -953,34 +1126,86 @@ csched_load_balance(int cpu, struct csched_vcpu *snext)
continue;
}
+ peer_vcpu = per_cpu(schedule_data, peer_cpu).curr;
spc = CSCHED_PCPU(peer_cpu);
+
if ( unlikely(spc == NULL) )
{
CSCHED_STAT_CRANK(steal_peer_down);
- speer = NULL;
}
- else if ( is_idle_vcpu(per_cpu(schedule_data, peer_cpu).curr) )
+ else if ( unlikely(is_idle_vcpu(peer_vcpu)) )
{
+ /*
+ * Don't steal from an idle CPU's runq because it's about to
+ * pick up work from it itself.
+ */
CSCHED_STAT_CRANK(steal_peer_idle);
- speer = NULL;
+ }
+ else if ( is_idle_vcpu(__runq_elem(spc->runq.next)->vcpu) )
+ {
+ if ( snext->pri == CSCHED_PRI_IDLE &&
+ __csched_running_vcpu_is_stealable(cpu, peer_vcpu) )
+ {
+ CSCHED_STAT_CRANK(steal_loner_candidate);
+ cpu_set(peer_cpu, loners);
+ }
}
else
{
- /* Try to steal work from an online non-idle CPU. */
+ /* Try to steal work from a remote CPU's runq. */
speer = csched_runq_steal(spc, cpu, snext->pri);
+ if ( speer != NULL )
+ {
+ spin_unlock(&per_cpu(schedule_data, peer_cpu).schedule_lock);
+ CSCHED_STAT_CRANK(vcpu_migrate);
+ speer->stats.migrate++;
+ return speer;
+ }
}
spin_unlock(&per_cpu(schedule_data, peer_cpu).schedule_lock);
+ }
- /* Got one? */
- if ( speer )
+ /*
+ * If we failed to find any remotely queued VCPUs to move here,
+ * see if it would be more efficient to move any of the running
+ * remote VCPUs over here.
+ */
+ while ( !cpus_empty(loners) )
+ {
+ /* For each CPU of interest, starting with our neighbour... */
+ peer_cpu = next_cpu(peer_cpu, loners);
+ if ( peer_cpu == NR_CPUS )
+ peer_cpu = first_cpu(loners);
+
+ cpu_clear(peer_cpu, loners);
+
+ if ( !spin_trylock(&per_cpu(schedule_data, peer_cpu).schedule_lock) )
{
- CSCHED_STAT_CRANK(vcpu_migrate);
- return speer;
+ CSCHED_STAT_CRANK(steal_trylock_failed);
+ continue;
}
+
+ peer_vcpu = per_cpu(schedule_data, peer_cpu).curr;
+ spc = CSCHED_PCPU(peer_cpu);
+
+ /* Signal the first candidate only. */
+ if ( !is_idle_vcpu(peer_vcpu) &&
+ is_idle_vcpu(__runq_elem(spc->runq.next)->vcpu) &&
+ __csched_running_vcpu_is_stealable(cpu, peer_vcpu) )
+ {
+ set_bit(_VCPUF_migrating, &peer_vcpu->vcpu_flags);
+ spin_unlock(&per_cpu(schedule_data, peer_cpu).schedule_lock);
+
+ CSCHED_STAT_CRANK(steal_loner_signal);
+ cpu_raise_softirq(peer_cpu, SCHEDULE_SOFTIRQ);
+ break;
+ }
+
+ spin_unlock(&per_cpu(schedule_data, peer_cpu).schedule_lock);
}
- /* Failed to find more important work */
+ /* Failed to find more important work elsewhere... */
__runq_remove(snext);
return snext;
}
@@ -1041,7 +1266,7 @@ csched_schedule(s_time_t now)
/*
* Return task to run next...
*/
- ret.time = MILLISECS(CSCHED_TSLICE);
+ ret.time = MILLISECS(CSCHED_MSECS_PER_TSLICE);
ret.task = snext->vcpu;
CSCHED_VCPU_CHECK(ret.task);
@@ -1062,12 +1287,13 @@ csched_dump_vcpu(struct csched_vcpu *svc)
if ( sdom )
{
- printk(" credit=%i (%d+%u) {a=%u i=%u w=%u}",
+ printk(" credit=%i (%d+%u) {a/i=%u/%u m=%u w=%u}",
atomic_read(&svc->credit),
- svc->credit_last,
- svc->credit_incr,
- svc->state_active,
- svc->state_idle,
+ svc->stats.credit_last,
+ svc->stats.credit_incr,
+ svc->stats.state_active,
+ svc->stats.state_idle,
+ svc->stats.migrate,
sdom->weight);
}
@@ -1085,9 +1311,11 @@ csched_dump_pcpu(int cpu)
spc = CSCHED_PCPU(cpu);
runq = &spc->runq;
- printk(" tick=%lu, sort=%d\n",
+ printk(" tick=%lu, sort=%d, sibling=0x%lx, core=0x%lx\n",
per_cpu(schedule_data, cpu).tick,
- spc->runq_sort_last);
+ spc->runq_sort_last,
+ cpu_sibling_map[cpu].bits[0],
+ cpu_core_map[cpu].bits[0]);
/* current VCPU */
svc = CSCHED_VCPU(per_cpu(schedule_data, cpu).curr);
@@ -1122,20 +1350,22 @@ csched_dump(void)
"\tcredit balance = %d\n"
"\tweight = %u\n"
"\trunq_sort = %u\n"
- "\ttick = %dms\n"
- "\ttslice = %dms\n"
- "\taccounting period = %dms\n"
- "\tdefault-weight = %d\n",
+ "\tdefault-weight = %d\n"
+ "\tmsecs per tick = %dms\n"
+ "\tcredits per tick = %d\n"
+ "\tticks per tslice = %d\n"
+ "\tticks per acct = %d\n",
csched_priv.ncpus,
csched_priv.master,
csched_priv.credit,
csched_priv.credit_balance,
csched_priv.weight,
csched_priv.runq_sort,
- CSCHED_TICK,
- CSCHED_TSLICE,
- CSCHED_ACCT_PERIOD,
- CSCHED_DEFAULT_WEIGHT);
+ CSCHED_DEFAULT_WEIGHT,
+ CSCHED_MSECS_PER_TICK,
+ CSCHED_CREDITS_PER_TICK,
+ CSCHED_TICKS_PER_TSLICE,
+ CSCHED_TICKS_PER_ACCT);
printk("idlers: 0x%lx\n", csched_priv.idlers.bits[0]);
@@ -1180,14 +1410,18 @@ struct scheduler sched_credit_def = {
.opt_name = "credit",
.sched_id = XEN_SCHEDULER_CREDIT,
- .init_vcpu = csched_vcpu_init,
+ .init_domain = csched_dom_init,
.destroy_domain = csched_dom_destroy,
+ .init_vcpu = csched_vcpu_init,
+ .destroy_vcpu = csched_vcpu_destroy,
+
.sleep = csched_vcpu_sleep,
.wake = csched_vcpu_wake,
.adjust = csched_dom_cntl,
+ .pick_cpu = csched_cpu_pick,
.tick = csched_tick,
.do_schedule = csched_schedule,
diff --git a/xen/common/sched_sedf.c b/xen/common/sched_sedf.c
index 3e041d860e..5381fcb3ca 100644
--- a/xen/common/sched_sedf.c
+++ b/xen/common/sched_sedf.c
@@ -333,14 +333,6 @@ static int sedf_init_vcpu(struct vcpu *v)
{
struct sedf_vcpu_info *inf;
- if ( v->domain->sched_priv == NULL )
- {
- v->domain->sched_priv = xmalloc(struct sedf_dom_info);
- if ( v->domain->sched_priv == NULL )
- return -1;
- memset(v->domain->sched_priv, 0, sizeof(struct sedf_dom_info));
- }
-
if ( (v->sched_priv = xmalloc(struct sedf_vcpu_info)) == NULL )
return -1;
memset(v->sched_priv, 0, sizeof(struct sedf_vcpu_info));
@@ -398,15 +390,33 @@ static int sedf_init_vcpu(struct vcpu *v)
return 0;
}
-static void sedf_destroy_domain(struct domain *d)
+static void sedf_destroy_vcpu(struct vcpu *v)
{
- int i;
+ xfree(v->sched_priv);
+}
+
+static int sedf_init_domain(struct domain *d)
+{
+ d->sched_priv = xmalloc(struct sedf_dom_info);
+ if ( d->sched_priv == NULL )
+ return -ENOMEM;
+
+ memset(d->sched_priv, 0, sizeof(struct sedf_dom_info));
+
+ return 0;
+}
+static void sedf_destroy_domain(struct domain *d)
+{
xfree(d->sched_priv);
-
- for ( i = 0; i < MAX_VIRT_CPUS; i++ )
- if ( d->vcpu[i] )
- xfree(d->vcpu[i]->sched_priv);
+}
+
+static int sedf_pick_cpu(struct vcpu *v)
+{
+ cpumask_t online_affinity;
+
+ cpus_and(online_affinity, v->cpu_affinity, cpu_online_map);
+ return first_cpu(online_affinity);
}
/*
@@ -1427,10 +1437,14 @@ struct scheduler sched_sedf_def = {
.opt_name = "sedf",
.sched_id = XEN_SCHEDULER_SEDF,
- .init_vcpu = sedf_init_vcpu,
+ .init_domain = sedf_init_domain,
.destroy_domain = sedf_destroy_domain,
+ .init_vcpu = sedf_init_vcpu,
+ .destroy_vcpu = sedf_destroy_vcpu,
+
.do_schedule = sedf_do_schedule,
+ .pick_cpu = sedf_pick_cpu,
.dump_cpu_state = sedf_dump_cpu_state,
.sleep = sedf_sleep,
.wake = sedf_wake,
diff --git a/xen/common/schedule.c b/xen/common/schedule.c
index fc2a927e2b..b333406d8a 100644
--- a/xen/common/schedule.c
+++ b/xen/common/schedule.c
@@ -132,17 +132,20 @@ int sched_init_vcpu(struct vcpu *v, unsigned int processor)
return SCHED_OP(init_vcpu, v);
}
-void sched_destroy_domain(struct domain *d)
+void sched_destroy_vcpu(struct vcpu *v)
{
- struct vcpu *v;
+ kill_timer(&v->timer);
+ kill_timer(&v->poll_timer);
+ SCHED_OP(destroy_vcpu, v);
+}
- for_each_vcpu ( d, v )
- {
- kill_timer(&v->timer);
- kill_timer(&v->poll_timer);
- TRACE_2D(TRC_SCHED_DOM_REM, v->domain->domain_id, v->vcpu_id);
- }
+int sched_init_domain(struct domain *d)
+{
+ return SCHED_OP(init_domain, d);
+}
+void sched_destroy_domain(struct domain *d)
+{
SCHED_OP(destroy_domain, d);
}
@@ -200,7 +203,6 @@ void vcpu_wake(struct vcpu *v)
static void vcpu_migrate(struct vcpu *v)
{
- cpumask_t online_affinity;
unsigned long flags;
int old_cpu;
@@ -215,8 +217,7 @@ static void vcpu_migrate(struct vcpu *v)
/* Switch to new CPU, then unlock old CPU. */
old_cpu = v->processor;
- cpus_and(online_affinity, v->cpu_affinity, cpu_online_map);
- v->processor = first_cpu(online_affinity);
+ v->processor = SCHED_OP(pick_cpu, v);
spin_unlock_irqrestore(
&per_cpu(schedule_data, old_cpu).schedule_lock, flags);
@@ -468,7 +469,7 @@ long do_set_timer_op(s_time_t timeout)
* timeout in this case can burn a lot of CPU. We therefore go for a
* reasonable middleground of triggering a timer event in 100ms.
*/
- DPRINTK("Warning: huge timeout set by domain %d (vcpu %d):"
+ gdprintk(XENLOG_INFO, "Warning: huge timeout set by domain %d (vcpu %d):"
" %"PRIx64"\n",
v->domain->domain_id, v->vcpu_id, (uint64_t)timeout);
set_timer(&v->timer, NOW() + MILLISECS(100));
diff --git a/xen/common/trace.c b/xen/common/trace.c
index 5f40a6b9d2..a5cec8e69a 100644
--- a/xen/common/trace.c
+++ b/xen/common/trace.c
@@ -131,7 +131,7 @@ static int tb_set_size(int size)
*/
if ( (opt_tbuf_size != 0) || (size <= 0) )
{
- DPRINTK("tb_set_size from %d to %d not implemented\n",
+ gdprintk(XENLOG_INFO, "tb_set_size from %d to %d not implemented\n",
opt_tbuf_size, size);
return -EINVAL;
}
diff --git a/xen/common/xmalloc.c b/xen/common/xmalloc.c
index 019bf9987a..60f75d60b1 100644
--- a/xen/common/xmalloc.c
+++ b/xen/common/xmalloc.c
@@ -34,16 +34,81 @@
#include <xen/cache.h>
#include <xen/prefetch.h>
+/*
+ * XMALLOC_DEBUG:
+ * 1. Free data blocks are filled with poison bytes.
+ * 2. In-use data blocks have guard bytes at the start and end.
+ */
+#ifndef NDEBUG
+#define XMALLOC_DEBUG 1
+#endif
+
static LIST_HEAD(freelist);
static DEFINE_SPINLOCK(freelist_lock);
struct xmalloc_hdr
{
- /* Total including this hdr. */
+ /* Size is total including this header. */
size_t size;
struct list_head freelist;
} __cacheline_aligned;
+static void add_to_freelist(struct xmalloc_hdr *hdr)
+{
+#if XMALLOC_DEBUG
+ memset(hdr + 1, 0xa5, hdr->size - sizeof(*hdr));
+#endif
+ list_add(&hdr->freelist, &freelist);
+}
+
+static void del_from_freelist(struct xmalloc_hdr *hdr)
+{
+#if XMALLOC_DEBUG
+ size_t i;
+ unsigned char *data = (unsigned char *)(hdr + 1);
+ for ( i = 0; i < (hdr->size - sizeof(*hdr)); i++ )
+ BUG_ON(data[i] != 0xa5);
+ BUG_ON((hdr->size <= 0) || (hdr->size >= PAGE_SIZE));
+#endif
+ list_del(&hdr->freelist);
+}
+
+static void *data_from_header(struct xmalloc_hdr *hdr)
+{
+#if XMALLOC_DEBUG
+ /* Data block contain SMP_CACHE_BYTES of guard canary. */
+ unsigned char *data = (unsigned char *)(hdr + 1);
+ memset(data, 0x5a, SMP_CACHE_BYTES);
+ memset(data + hdr->size - sizeof(*hdr) - SMP_CACHE_BYTES,
+ 0x5a, SMP_CACHE_BYTES);
+ return data + SMP_CACHE_BYTES;
+#else
+ return hdr + 1;
+#endif
+}
+
+static struct xmalloc_hdr *header_from_data(const void *p)
+{
+#if XMALLOC_DEBUG
+ unsigned char *data = (unsigned char *)p - SMP_CACHE_BYTES;
+ struct xmalloc_hdr *hdr = (struct xmalloc_hdr *)data - 1;
+ size_t i;
+
+ /* Check header guard canary. */
+ for ( i = 0; i < SMP_CACHE_BYTES; i++ )
+ BUG_ON(data[i] != 0x5a);
+
+ /* Check footer guard canary. */
+ data += hdr->size - sizeof(*hdr) - SMP_CACHE_BYTES;
+ for ( i = 0; i < SMP_CACHE_BYTES; i++ )
+ BUG_ON(data[i] != 0x5a);
+
+ return hdr;
+#else
+ return (struct xmalloc_hdr *)p - 1;
+#endif
+}
+
static void maybe_split(struct xmalloc_hdr *hdr, size_t size, size_t block)
{
struct xmalloc_hdr *extra;
@@ -54,7 +119,7 @@ static void maybe_split(struct xmalloc_hdr *hdr, size_t size, size_t block)
{
extra = (struct xmalloc_hdr *)((unsigned long)hdr + size);
extra->size = leftover;
- list_add(&extra->freelist, &freelist);
+ add_to_freelist(extra);
}
else
{
@@ -79,7 +144,7 @@ static void *xmalloc_new_page(size_t size)
maybe_split(hdr, size, PAGE_SIZE);
spin_unlock_irqrestore(&freelist_lock, flags);
- return hdr+1;
+ return data_from_header(hdr);
}
/* Big object? Just use the page allocator. */
@@ -96,7 +161,7 @@ static void *xmalloc_whole_pages(size_t size)
/* Debugging aid. */
hdr->freelist.next = hdr->freelist.prev = NULL;
- return hdr+1;
+ return data_from_header(hdr);
}
/* Return size, increased to alignment with align. */
@@ -113,6 +178,11 @@ void *_xmalloc(size_t size, size_t align)
/* We currently always return cacheline aligned. */
BUG_ON(align > SMP_CACHE_BYTES);
+#if XMALLOC_DEBUG
+ /* Add room for canaries at start and end of data block. */
+ size += 2 * SMP_CACHE_BYTES;
+#endif
+
/* Add room for header, pad to align next header. */
size += sizeof(struct xmalloc_hdr);
size = align_up(size, __alignof__(struct xmalloc_hdr));
@@ -127,10 +197,10 @@ void *_xmalloc(size_t size, size_t align)
{
if ( i->size < size )
continue;
- list_del(&i->freelist);
+ del_from_freelist(i);
maybe_split(i, size, i->size);
spin_unlock_irqrestore(&freelist_lock, flags);
- return i+1;
+ return data_from_header(i);
}
spin_unlock_irqrestore(&freelist_lock, flags);
@@ -146,7 +216,7 @@ void xfree(const void *p)
if ( p == NULL )
return;
- hdr = (struct xmalloc_hdr *)p - 1;
+ hdr = header_from_data(p);
/* We know hdr will be on same page. */
BUG_ON(((long)p & PAGE_MASK) != ((long)hdr & PAGE_MASK));
@@ -175,7 +245,7 @@ void xfree(const void *p)
/* We follow this block? Swallow it. */
if ( (_i + i->size) == _hdr )
{
- list_del(&i->freelist);
+ del_from_freelist(i);
i->size += hdr->size;
hdr = i;
}
@@ -183,7 +253,7 @@ void xfree(const void *p)
/* We precede this block? Swallow it. */
if ( (_hdr + hdr->size) == _i )
{
- list_del(&i->freelist);
+ del_from_freelist(i);
hdr->size += i->size;
}
}
@@ -196,7 +266,7 @@ void xfree(const void *p)
}
else
{
- list_add(&hdr->freelist, &freelist);
+ add_to_freelist(hdr);
}
spin_unlock_irqrestore(&freelist_lock, flags);
diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c
index 235d6fefd8..6a97fc2965 100644
--- a/xen/drivers/char/console.c
+++ b/xen/drivers/char/console.c
@@ -4,6 +4,10 @@
* Emergency console I/O for Xen and the domain-0 guest OS.
*
* Copyright (c) 2002-2004, K A Fraser.
+ *
+ * Added printf_ratelimit
+ * Taken from Linux - Author: Andi Kleen (net_ratelimit)
+ * Ported to Xen - Steven Rostedt - Red Hat
*/
#include <xen/stdarg.h>
@@ -26,6 +30,7 @@
#include <asm/current.h>
#include <asm/debugger.h>
#include <asm/io.h>
+#include <asm/div64.h>
/* console: comma-separated list of console outputs. */
static char opt_console[30] = OPT_CONSOLE_STR;
@@ -54,6 +59,100 @@ static int sercon_handle = -1;
static DEFINE_SPINLOCK(console_lock);
/*
+ * To control the amount of printing, thresholds are added.
+ * These thresholds correspond to the XENLOG logging levels.
+ * There's an upper and lower threshold for non-guest messages and for
+ * guest-provoked messages. This works as follows, for a given log level L:
+ *
+ * L < lower_threshold : always logged
+ * lower_threshold <= L < upper_threshold : rate-limited logging
+ * upper_threshold <= L : never logged
+ *
+ * Note, in the above algorithm, to disable rate limiting simply make
+ * the lower threshold equal to the upper.
+ */
+#define XENLOG_UPPER_THRESHOLD 2 /* Do not print INFO and DEBUG */
+#define XENLOG_LOWER_THRESHOLD 2 /* Always print ERR and WARNING */
+#define XENLOG_GUEST_UPPER_THRESHOLD 2 /* Do not print INFO and DEBUG */
+#define XENLOG_GUEST_LOWER_THRESHOLD 0 /* Rate-limit ERR and WARNING */
+/*
+ * The XENLOG_DEFAULT is the default given to printks that
+ * do not have any print level associated with them.
+ */
+#define XENLOG_DEFAULT 1 /* XENLOG_WARNING */
+#define XENLOG_GUEST_DEFAULT 1 /* XENLOG_WARNING */
+
+static int xenlog_upper_thresh = XENLOG_UPPER_THRESHOLD;
+static int xenlog_lower_thresh = XENLOG_LOWER_THRESHOLD;
+static int xenlog_guest_upper_thresh = XENLOG_GUEST_UPPER_THRESHOLD;
+static int xenlog_guest_lower_thresh = XENLOG_GUEST_LOWER_THRESHOLD;
+
+static void parse_loglvl(char *s);
+static void parse_guest_loglvl(char *s);
+
+/*
+ * <lvl> := none|error|warning|info|debug|all
+ * loglvl=<lvl_print_always>[/<lvl_print_ratelimit>]
+ * <lvl_print_always>: log level which is always printed
+ * <lvl_print_rlimit>: log level which is rate-limit printed
+ * Similar definitions for guest_loglvl, but applies to guest tracing.
+ * Defaults: loglvl=warning ; guest_loglvl=none/warning
+ */
+custom_param("loglvl", parse_loglvl);
+custom_param("guest_loglvl", parse_guest_loglvl);
+
+static atomic_t print_everything = ATOMIC_INIT(1);
+
+#define ___parse_loglvl(s, ps, lvlstr, lvlnum) \
+ if ( !strncmp((s), (lvlstr), strlen(lvlstr)) ) { \
+ *(ps) = (s) + strlen(lvlstr); \
+ return (lvlnum); \
+ }
+
+static int __parse_loglvl(char *s, char **ps)
+{
+ ___parse_loglvl(s, ps, "none", 0);
+ ___parse_loglvl(s, ps, "error", 1);
+ ___parse_loglvl(s, ps, "warning", 2);
+ ___parse_loglvl(s, ps, "info", 3);
+ ___parse_loglvl(s, ps, "debug", 4);
+ ___parse_loglvl(s, ps, "all", 4);
+ return 2; /* sane fallback */
+}
+
+static void _parse_loglvl(char *s, int *lower, int *upper)
+{
+ *lower = *upper = __parse_loglvl(s, &s);
+ if ( *s == '/' )
+ *upper = __parse_loglvl(s+1, &s);
+ if ( *upper < *lower )
+ *upper = *lower;
+}
+
+static void parse_loglvl(char *s)
+{
+ _parse_loglvl(s, &xenlog_lower_thresh, &xenlog_upper_thresh);
+}
+
+static void parse_guest_loglvl(char *s)
+{
+ _parse_loglvl(s, &xenlog_guest_lower_thresh, &xenlog_guest_upper_thresh);
+}
+
+static char *loglvl_str(int lvl)
+{
+ switch ( lvl )
+ {
+ case 0: return "Nothing";
+ case 1: return "Errors";
+ case 2: return "Errors and warnings";
+ case 3: return "Errors, warnings and info";
+ case 4: return "All";
+ }
+ return "???";
+}
+
+/*
* ********************************************************
* *************** ACCESS TO CONSOLE RING *****************
* ********************************************************
@@ -281,7 +380,7 @@ long do_console_io(int cmd, int count, XEN_GUEST_HANDLE(char) buffer)
* *****************************************************
*/
-static inline void __putstr(const char *str)
+static void __putstr(const char *str)
{
int c;
@@ -294,29 +393,70 @@ static inline void __putstr(const char *str)
}
}
+static int printk_prefix_check(char *p, char **pp)
+{
+ int loglvl = -1;
+ int upper_thresh = xenlog_upper_thresh;
+ int lower_thresh = xenlog_lower_thresh;
+
+ while ( (p[0] == '<') && (p[1] != '\0') && (p[2] == '>') )
+ {
+ switch ( p[1] )
+ {
+ case 'G':
+ upper_thresh = xenlog_guest_upper_thresh;
+ lower_thresh = xenlog_guest_lower_thresh;
+ if ( loglvl == -1 )
+ loglvl = XENLOG_GUEST_DEFAULT;
+ break;
+ case '0' ... '3':
+ loglvl = p[1] - '0';
+ break;
+ }
+ p += 3;
+ }
+
+ if ( loglvl == -1 )
+ loglvl = XENLOG_DEFAULT;
+
+ *pp = p;
+
+ return ((atomic_read(&print_everything) != 0) ||
+ (loglvl < lower_thresh) ||
+ ((loglvl < upper_thresh) && printk_ratelimit()));
+}
+
void printk(const char *fmt, ...)
{
static char buf[1024];
- static int start_of_line = 1;
+ static int start_of_line = 1, do_print;
va_list args;
char *p, *q;
unsigned long flags;
- spin_lock_irqsave(&console_lock, flags);
+ /* console_lock can be acquired recursively from __printk_ratelimit(). */
+ local_irq_save(flags);
+ spin_lock_recursive(&console_lock);
va_start(args, fmt);
(void)vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
p = buf;
+
while ( (q = strchr(p, '\n')) != NULL )
{
*q = '\0';
if ( start_of_line )
- __putstr(printk_prefix);
- __putstr(p);
- __putstr("\n");
+ do_print = printk_prefix_check(p, &p);
+ if ( do_print )
+ {
+ if ( start_of_line )
+ __putstr(printk_prefix);
+ __putstr(p);
+ __putstr("\n");
+ }
start_of_line = 1;
p = q + 1;
}
@@ -324,12 +464,18 @@ void printk(const char *fmt, ...)
if ( *p != '\0' )
{
if ( start_of_line )
- __putstr(printk_prefix);
- __putstr(p);
+ do_print = printk_prefix_check(p, &p);
+ if ( do_print )
+ {
+ if ( start_of_line )
+ __putstr(printk_prefix);
+ __putstr(p);
+ }
start_of_line = 0;
}
- spin_unlock_irqrestore(&console_lock, flags);
+ spin_unlock_recursive(&console_lock);
+ local_irq_restore(flags);
}
void set_printk_prefix(const char *prefix)
@@ -377,10 +523,18 @@ void console_endboot(void)
{
int i, j;
+ printk("Std. Loglevel: %s", loglvl_str(xenlog_lower_thresh));
+ if ( xenlog_upper_thresh != xenlog_lower_thresh )
+ printk(" (Rate-limited: %s)", loglvl_str(xenlog_upper_thresh));
+ printk("\nGuest Loglevel: %s", loglvl_str(xenlog_guest_lower_thresh));
+ if ( xenlog_guest_upper_thresh != xenlog_guest_lower_thresh )
+ printk(" (Rate-limited: %s)", loglvl_str(xenlog_guest_upper_thresh));
+ printk("\n");
+
if ( opt_sync_console )
{
printk("**********************************************\n");
- printk("******* WARNING: CONSOLE OUTPUT IS SYCHRONOUS\n");
+ printk("******* WARNING: CONSOLE OUTPUT IS SYNCHRONOUS\n");
printk("******* This option is intended to aid debugging "
"of Xen by ensuring\n");
printk("******* that all output is synchronously delivered "
@@ -414,6 +568,19 @@ void console_endboot(void)
/* Serial input is directed to DOM0 by default. */
switch_serial_input();
+
+ /* Now we implement the logging thresholds. */
+ console_end_log_everything();
+}
+
+void console_start_log_everything(void)
+{
+ atomic_inc(&print_everything);
+}
+
+void console_end_log_everything(void)
+{
+ atomic_dec(&print_everything);
}
void console_force_unlock(void)
@@ -430,12 +597,14 @@ void console_force_lock(void)
void console_start_sync(void)
{
+ console_start_log_everything();
serial_start_sync(sercon_handle);
}
void console_end_sync(void)
{
serial_end_sync(sercon_handle);
+ console_end_log_everything();
}
void console_putc(char c)
@@ -448,6 +617,66 @@ int console_getc(void)
return serial_getc(sercon_handle);
}
+/*
+ * printk rate limiting, lifted from Linux.
+ *
+ * This enforces a rate limit: not more than one kernel message
+ * every printk_ratelimit_ms (millisecs).
+ */
+int __printk_ratelimit(int ratelimit_ms, int ratelimit_burst)
+{
+ static DEFINE_SPINLOCK(ratelimit_lock);
+ static unsigned long toks = 10 * 5 * 1000;
+ static unsigned long last_msg;
+ static int missed;
+ unsigned long flags;
+ unsigned long long now = NOW(); /* ns */
+ unsigned long ms;
+
+ do_div(now, 1000000);
+ ms = (unsigned long)now;
+
+ spin_lock_irqsave(&ratelimit_lock, flags);
+ toks += ms - last_msg;
+ last_msg = ms;
+ if ( toks > (ratelimit_burst * ratelimit_ms))
+ toks = ratelimit_burst * ratelimit_ms;
+ if ( toks >= ratelimit_ms )
+ {
+ int lost = missed;
+ missed = 0;
+ toks -= ratelimit_ms;
+ spin_unlock(&ratelimit_lock);
+ if ( lost )
+ {
+ char lost_str[8];
+ snprintf(lost_str, sizeof(lost_str), "%d", lost);
+ /* console_lock may already be acquired by printk(). */
+ spin_lock_recursive(&console_lock);
+ __putstr(printk_prefix);
+ __putstr("printk: ");
+ __putstr(lost_str);
+ __putstr(" messages suppressed.\n");
+ spin_unlock_recursive(&console_lock);
+ }
+ local_irq_restore(flags);
+ return 1;
+ }
+ missed++;
+ spin_unlock_irqrestore(&ratelimit_lock, flags);
+ return 0;
+}
+
+/* minimum time in ms between messages */
+int printk_ratelimit_ms = 5 * 1000;
+
+/* number of messages we send before ratelimiting */
+int printk_ratelimit_burst = 10;
+
+int printk_ratelimit(void)
+{
+ return __printk_ratelimit(printk_ratelimit_ms, printk_ratelimit_burst);
+}
/*
* **************************************************************
@@ -494,9 +723,10 @@ static void debugtrace_toggle(void)
watchdog_disable();
spin_lock_irqsave(&debugtrace_lock, flags);
- // dump the buffer *before* toggling, in case the act of dumping the
- // buffer itself causes more printk's...
- //
+ /*
+ * Dump the buffer *before* toggling, in case the act of dumping the
+ * buffer itself causes more printk() invocations.
+ */
printk("debugtrace_printk now writing to %s.\n",
!debugtrace_send_to_console ? "console": "buffer");
if ( !debugtrace_send_to_console )
@@ -650,9 +880,9 @@ void panic(const char *fmt, ...)
void __bug(char *file, int line)
{
console_start_sync();
- debugtrace_dump();
printk("BUG at %s:%d\n", file, line);
- FORCE_CRASH();
+ dump_execution_state();
+ panic("BUG at %s:%d\n", file, line);
for ( ; ; ) ;
}
diff --git a/xen/include/asm-ia64/config.h b/xen/include/asm-ia64/config.h
index 9cb2aeaea3..92bfd364b1 100644
--- a/xen/include/asm-ia64/config.h
+++ b/xen/include/asm-ia64/config.h
@@ -271,13 +271,6 @@ struct screen_info { };
#endif /* __ASSEMBLY__ */
#endif /* __XEN_IA64_CONFIG_H__ */
-#ifndef __ASSEMBLY__
-#include <linux/linkage.h>
-#define FORCE_CRASH() asm("break.m 0;;");
-#else
-#define FORCE_CRASH break.m 0;;
-#endif
-
/* Allow .serialize.data/instruction in asm files.
Old as doesn't handle this. */
#define HAVE_SERIALIZE_DIRECTIVE
diff --git a/xen/include/asm-ia64/debugger.h b/xen/include/asm-ia64/debugger.h
index e320ff68cc..104cea9bfc 100644
--- a/xen/include/asm-ia64/debugger.h
+++ b/xen/include/asm-ia64/debugger.h
@@ -23,7 +23,6 @@
#define __ASM_DEBUGGER_H__
// this number is an arbitary number which is not used for any other purpose
-// __builtin_trap(), FORCE_CRASH() 0x0
// ski 0x80001, 0x80002
// kdb 0x80100, 0x80101
// kprobe 0x80200, jprobe 0x80300
diff --git a/xen/include/asm-ia64/mm.h b/xen/include/asm-ia64/mm.h
index 26266942b5..4a7fa50492 100644
--- a/xen/include/asm-ia64/mm.h
+++ b/xen/include/asm-ia64/mm.h
@@ -175,7 +175,7 @@ static inline int get_page(struct page_info *page,
unlikely((nx & PGC_count_mask) == 0) || /* Count overflow? */
unlikely((x >> 32) != _domain)) { /* Wrong owner? */
- DPRINTK("Error pfn %lx: rd=%p, od=%p, caf=%016lx, taf=%"
+ gdprintk(XENLOG_INFO, "Error pfn %lx: rd=%p, od=%p, caf=%016lx, taf=%"
PRtype_info "\n", page_to_mfn(page), domain,
unpickle_domptr(x >> 32), x, page->u.inuse.type_info);
return 0;
diff --git a/xen/include/asm-ia64/vlsapic.h b/xen/include/asm-ia64/vlsapic.h
index aceee921f3..98bc88f9df 100644
--- a/xen/include/asm-ia64/vlsapic.h
+++ b/xen/include/asm-ia64/vlsapic.h
@@ -24,7 +24,6 @@
#define _LSAPIC_H
#include <xen/sched.h>
-extern void vmx_virq_line_init(struct domain *d);
extern void vtm_init(struct vcpu *vcpu);
extern void vtm_set_itc(struct vcpu *vcpu, uint64_t new_itc);
extern void vtm_set_itm(struct vcpu *vcpu, uint64_t val);
diff --git a/xen/include/asm-ia64/vmx.h b/xen/include/asm-ia64/vmx.h
index 577fc1e34d..4e0971278a 100644
--- a/xen/include/asm-ia64/vmx.h
+++ b/xen/include/asm-ia64/vmx.h
@@ -44,13 +44,11 @@ extern void show_registers(struct pt_regs *regs);
#define show_execution_state show_registers
extern unsigned long __gpfn_to_mfn_foreign(struct domain *d, unsigned long gpfn);
extern void sync_split_caches(void);
-extern void vmx_virq_line_assist(struct vcpu *v);
extern void set_privileged_operation_isr (struct vcpu *vcpu,int inst);
extern void privilege_op (struct vcpu *vcpu);
extern void set_ifa_itir_iha (struct vcpu *vcpu, u64 vadr,
int set_ifa, int set_itir, int set_iha);
extern void inject_guest_interruption(struct vcpu *vcpu, u64 vec);
-extern void vmx_intr_assist(struct vcpu *v);
extern void set_illegal_op_isr (struct vcpu *vcpu);
extern void illegal_op (struct vcpu *vcpu);
extern void vmx_relinquish_guest_resources(struct domain *d);
diff --git a/xen/include/asm-ia64/vmx_platform.h b/xen/include/asm-ia64/vmx_platform.h
index 07d05a68c6..7239fd1793 100644
--- a/xen/include/asm-ia64/vmx_platform.h
+++ b/xen/include/asm-ia64/vmx_platform.h
@@ -32,7 +32,7 @@ typedef struct virtual_platform_def {
unsigned long params[HVM_NR_PARAMS];
struct mmio_list *mmio;
/* One IOSAPIC now... */
- struct hvm_vioapic vioapic;
+ struct vioapic vioapic;
} vir_plat_t;
static inline int __fls(uint32_t word)
@@ -55,7 +55,7 @@ typedef struct vlapic {
extern uint64_t dummy_tmr[];
#define VLAPIC_ID(l) (uint16_t)(((l)->vcpu->arch.privregs->lid) >> 16)
#define VLAPIC_IRR(l) ((l)->vcpu->arch.privregs->irr[0])
-struct vlapic* apic_round_robin(struct domain *d, uint8_t dest_mode, uint8_t vector, uint32_t bitmap);
+struct vlapic *apic_round_robin(struct domain *d, uint8_t vector, uint32_t bitmap);
extern int vmx_vcpu_pend_interrupt(struct vcpu *vcpu, uint8_t vector);
static inline int vlapic_set_irq(struct vlapic *t, uint8_t vec, uint8_t trig)
{
@@ -73,9 +73,7 @@ enum ioapic_irq_destination_types {
dest_ExtINT = 7
};
-/* As long as we register vlsapic to ioapic controller, it's said enabled */
#define vlapic_enabled(l) 1
-#define hvm_apic_support(d) 1
#define VLAPIC_DELIV_MODE_FIXED 0x0
#define VLAPIC_DELIV_MODE_REDIR 0x1
diff --git a/xen/include/asm-ia64/vmx_vpd.h b/xen/include/asm-ia64/vmx_vpd.h
index 3a5ad1ad15..17496d25f2 100644
--- a/xen/include/asm-ia64/vmx_vpd.h
+++ b/xen/include/asm-ia64/vmx_vpd.h
@@ -116,7 +116,6 @@ struct arch_vmx_struct {
#define VMX_DOMAIN(v) v->arch.arch_vmx.flags
#define ARCH_VMX_IO_WAIT 3 /* Waiting for I/O completion */
-#define ARCH_VMX_INTR_ASSIST 4 /* Need DM's assist to issue intr */
#define ARCH_VMX_DOMAIN 5 /* Need it to indicate VTi domain */
diff --git a/xen/include/asm-powerpc/powerpc64/config.h b/xen/include/asm-powerpc/powerpc64/config.h
index cf16dac479..86183d1441 100644
--- a/xen/include/asm-powerpc/powerpc64/config.h
+++ b/xen/include/asm-powerpc/powerpc64/config.h
@@ -36,10 +36,4 @@
#define HAS_FLOAT 1
#define HAS_VMX 1
-#ifndef __ASSEMBLY__
-
-#define FORCE_CRASH() __asm__ __volatile__ ( "trap" )
-
-#endif /* __ASSEMBLY__ */
-
#endif
diff --git a/xen/include/asm-x86/bitops.h b/xen/include/asm-x86/bitops.h
index 14d7e1451b..4c954663eb 100644
--- a/xen/include/asm-x86/bitops.h
+++ b/xen/include/asm-x86/bitops.h
@@ -14,10 +14,12 @@
#endif
/*
- * We use the "+m" constraint because the memory operand is both read from
- * and written to. Since the operand is in fact a word array, we also
- * specify "memory" in the clobbers list to indicate that words other than
- * the one directly addressed by the memory operand may be modified.
+ * We specify the memory operand as both input and output because the memory
+ * operand is both read from and written to. Since the operand is in fact a
+ * word array, we also specify "memory" in the clobbers list to indicate that
+ * words other than the one directly addressed by the memory operand may be
+ * modified. We don't use "+m" because the gcc manual says that it should be
+ * used only when the constraint allows the operand to reside in a register.
*/
#define ADDR (*(volatile long *) addr)
@@ -36,8 +38,8 @@ static __inline__ void set_bit(int nr, volatile void * addr)
{
__asm__ __volatile__( LOCK_PREFIX
"btsl %1,%0"
- :"+m" (ADDR)
- :"dIr" (nr) : "memory");
+ :"=m" (ADDR)
+ :"dIr" (nr), "m" (ADDR) : "memory");
}
/**
@@ -53,8 +55,8 @@ static __inline__ void __set_bit(int nr, volatile void * addr)
{
__asm__(
"btsl %1,%0"
- :"+m" (ADDR)
- :"dIr" (nr) : "memory");
+ :"=m" (ADDR)
+ :"dIr" (nr), "m" (ADDR) : "memory");
}
/**
@@ -71,8 +73,8 @@ static __inline__ void clear_bit(int nr, volatile void * addr)
{
__asm__ __volatile__( LOCK_PREFIX
"btrl %1,%0"
- :"+m" (ADDR)
- :"dIr" (nr) : "memory");
+ :"=m" (ADDR)
+ :"dIr" (nr), "m" (ADDR) : "memory");
}
/**
@@ -88,8 +90,8 @@ static __inline__ void __clear_bit(int nr, volatile void * addr)
{
__asm__(
"btrl %1,%0"
- :"+m" (ADDR)
- :"dIr" (nr) : "memory");
+ :"=m" (ADDR)
+ :"dIr" (nr), "m" (ADDR) : "memory");
}
#define smp_mb__before_clear_bit() barrier()
@@ -108,8 +110,8 @@ static __inline__ void __change_bit(int nr, volatile void * addr)
{
__asm__ __volatile__(
"btcl %1,%0"
- :"+m" (ADDR)
- :"dIr" (nr) : "memory");
+ :"=m" (ADDR)
+ :"dIr" (nr), "m" (ADDR) : "memory");
}
/**
@@ -125,8 +127,8 @@ static __inline__ void change_bit(int nr, volatile void * addr)
{
__asm__ __volatile__( LOCK_PREFIX
"btcl %1,%0"
- :"+m" (ADDR)
- :"dIr" (nr) : "memory");
+ :"=m" (ADDR)
+ :"dIr" (nr), "m" (ADDR) : "memory");
}
/**
@@ -143,8 +145,8 @@ static __inline__ int test_and_set_bit(int nr, volatile void * addr)
__asm__ __volatile__( LOCK_PREFIX
"btsl %2,%1\n\tsbbl %0,%0"
- :"=r" (oldbit),"+m" (ADDR)
- :"dIr" (nr) : "memory");
+ :"=r" (oldbit),"=m" (ADDR)
+ :"dIr" (nr), "m" (ADDR) : "memory");
return oldbit;
}
@@ -163,8 +165,8 @@ static __inline__ int __test_and_set_bit(int nr, volatile void * addr)
__asm__(
"btsl %2,%1\n\tsbbl %0,%0"
- :"=r" (oldbit),"+m" (ADDR)
- :"dIr" (nr) : "memory");
+ :"=r" (oldbit),"=m" (ADDR)
+ :"dIr" (nr), "m" (ADDR) : "memory");
return oldbit;
}
@@ -182,8 +184,8 @@ static __inline__ int test_and_clear_bit(int nr, volatile void * addr)
__asm__ __volatile__( LOCK_PREFIX
"btrl %2,%1\n\tsbbl %0,%0"
- :"=r" (oldbit),"+m" (ADDR)
- :"dIr" (nr) : "memory");
+ :"=r" (oldbit),"=m" (ADDR)
+ :"dIr" (nr), "m" (ADDR) : "memory");
return oldbit;
}
@@ -202,8 +204,8 @@ static __inline__ int __test_and_clear_bit(int nr, volatile void * addr)
__asm__(
"btrl %2,%1\n\tsbbl %0,%0"
- :"=r" (oldbit),"+m" (ADDR)
- :"dIr" (nr) : "memory");
+ :"=r" (oldbit),"=m" (ADDR)
+ :"dIr" (nr), "m" (ADDR) : "memory");
return oldbit;
}
@@ -214,8 +216,8 @@ static __inline__ int __test_and_change_bit(int nr, volatile void * addr)
__asm__ __volatile__(
"btcl %2,%1\n\tsbbl %0,%0"
- :"=r" (oldbit),"+m" (ADDR)
- :"dIr" (nr) : "memory");
+ :"=r" (oldbit),"=m" (ADDR)
+ :"dIr" (nr), "m" (ADDR) : "memory");
return oldbit;
}
@@ -233,8 +235,8 @@ static __inline__ int test_and_change_bit(int nr, volatile void * addr)
__asm__ __volatile__( LOCK_PREFIX
"btcl %2,%1\n\tsbbl %0,%0"
- :"=r" (oldbit),"+m" (ADDR)
- :"dIr" (nr) : "memory");
+ :"=r" (oldbit),"=m" (ADDR)
+ :"dIr" (nr), "m" (ADDR) : "memory");
return oldbit;
}
diff --git a/xen/include/asm-x86/config.h b/xen/include/asm-x86/config.h
index 879bdbf80b..f3b9cbe8b4 100644
--- a/xen/include/asm-x86/config.h
+++ b/xen/include/asm-x86/config.h
@@ -86,13 +86,6 @@
#ifndef __ASSEMBLY__
extern unsigned long _end; /* standard ELF symbol */
-
-static inline void FORCE_CRASH(void) __attribute__((noreturn,always_inline));
-static inline void FORCE_CRASH(void)
-{
- __asm__ __volatile__ ( "ud2" );
- while(1);
-}
#endif /* __ASSEMBLY__ */
#if defined(__x86_64__)
diff --git a/xen/include/asm-x86/grant_table.h b/xen/include/asm-x86/grant_table.h
index 6fa67ff883..9941a5c6f8 100644
--- a/xen/include/asm-x86/grant_table.h
+++ b/xen/include/asm-x86/grant_table.h
@@ -14,9 +14,9 @@
* must hold a reference to the page.
*/
int create_grant_host_mapping(
- unsigned long addr, unsigned long frame, unsigned int flags);
+ uint64_t addr, unsigned long frame, unsigned int flags);
int destroy_grant_host_mapping(
- unsigned long addr, unsigned long frame, unsigned int flags);
+ uint64_t addr, unsigned long frame, unsigned int flags);
#define gnttab_create_shared_page(d, t, i) \
do { \
diff --git a/xen/include/asm-x86/hvm/domain.h b/xen/include/asm-x86/hvm/domain.h
index 0ebec779c1..77c691e351 100644
--- a/xen/include/asm-x86/hvm/domain.h
+++ b/xen/include/asm-x86/hvm/domain.h
@@ -28,8 +28,6 @@
#include <asm/hvm/vioapic.h>
#include <public/hvm/params.h>
-#define HVM_PBUF_SIZE 80
-
struct hvm_domain {
unsigned long shared_page_va;
unsigned long buffered_io_va;
@@ -37,16 +35,18 @@ struct hvm_domain {
s64 tsc_frequency;
struct pl_time pl_time;
- struct hvm_virpic vpic;
- struct hvm_vioapic vioapic;
+ struct vpic vpic;
+ struct vioapic vioapic;
struct hvm_io_handler io_handler;
unsigned char round_info[256];
spinlock_t round_robin_lock;
int interrupt_request;
- int pbuf_index;
- char pbuf[HVM_PBUF_SIZE];
+ /* hvm_print_line() logging. */
+ char pbuf[80];
+ int pbuf_idx;
+ spinlock_t pbuf_lock;
uint64_t params[HVM_NR_PARAMS];
};
diff --git a/xen/include/asm-x86/hvm/hvm.h b/xen/include/asm-x86/hvm/hvm.h
index bc950677c6..6576e20839 100644
--- a/xen/include/asm-x86/hvm/hvm.h
+++ b/xen/include/asm-x86/hvm/hvm.h
@@ -33,10 +33,10 @@ struct hvm_function_table {
void (*disable)(void);
/*
- * Initialize/relinguish HVM guest resources
+ * Initialise/destroy HVM VCPU resources
*/
- int (*initialize_guest_resources)(struct vcpu *v);
- void (*relinquish_guest_resources)(struct domain *d);
+ int (*vcpu_initialise)(struct vcpu *v);
+ void (*vcpu_destroy)(struct vcpu *v);
/*
* Store and load guest state:
@@ -91,28 +91,11 @@ hvm_disable(void)
hvm_funcs.disable();
}
-void hvm_create_event_channels(struct vcpu *v);
-void hvm_map_io_shared_pages(struct vcpu *v);
+int hvm_domain_initialise(struct domain *d);
+void hvm_domain_destroy(struct domain *d);
-static inline int
-hvm_initialize_guest_resources(struct vcpu *v)
-{
- int ret = 1;
- if ( hvm_funcs.initialize_guest_resources )
- ret = hvm_funcs.initialize_guest_resources(v);
- if ( ret == 1 ) {
- hvm_map_io_shared_pages(v);
- hvm_create_event_channels(v);
- }
- return ret;
-}
-
-static inline void
-hvm_relinquish_guest_resources(struct domain *d)
-{
- if (hvm_funcs.relinquish_guest_resources)
- hvm_funcs.relinquish_guest_resources(d);
-}
+int hvm_vcpu_initialise(struct vcpu *v);
+void hvm_vcpu_destroy(struct vcpu *v);
static inline void
hvm_store_cpu_guest_regs(
diff --git a/xen/include/asm-x86/hvm/io.h b/xen/include/asm-x86/hvm/io.h
index 2e35ef79d9..b934938006 100644
--- a/xen/include/asm-x86/hvm/io.h
+++ b/xen/include/asm-x86/hvm/io.h
@@ -64,6 +64,7 @@
#define INSTR_BT 13
#define INSTR_XCHG 14
#define INSTR_SUB 15
+#define INSTR_ADD 16
#define MAX_INST_LEN 15 /* Maximum instruction length = 15 bytes */
@@ -115,8 +116,9 @@ struct hvm_mmio_handler {
/* global io interception point in HV */
extern int hvm_io_intercept(ioreq_t *p, int type);
-extern int register_io_handler(unsigned long addr, unsigned long size,
- intercept_action_t action, int type);
+extern int register_io_handler(
+ struct domain *d, unsigned long addr, unsigned long size,
+ intercept_action_t action, int type);
static inline int hvm_portio_intercept(ioreq_t *p)
{
@@ -126,11 +128,11 @@ static inline int hvm_portio_intercept(ioreq_t *p)
extern int hvm_mmio_intercept(ioreq_t *p);
extern int hvm_buffered_io_intercept(ioreq_t *p);
-static inline int register_portio_handler(unsigned long addr,
- unsigned long size,
- intercept_action_t action)
+static inline int register_portio_handler(
+ struct domain *d, unsigned long addr,
+ unsigned long size, intercept_action_t action)
{
- return register_io_handler(addr, size, action, HVM_PORTIO);
+ return register_io_handler(d, addr, size, action, HVM_PORTIO);
}
#if defined(__i386__) || defined(__x86_64__)
@@ -141,15 +143,13 @@ static inline int irq_masked(unsigned long eflags)
#endif
extern void send_pio_req(unsigned long port, unsigned long count, int size,
- long value, int dir, int df, int pvalid);
+ long value, int dir, int df, int value_is_ptr);
extern void handle_mmio(unsigned long gpa);
extern void hvm_interrupt_post(struct vcpu *v, int vector, int type);
extern void hvm_io_assist(struct vcpu *v);
extern void pic_irq_request(void *data, int level);
-extern void hvm_pic_assist(struct vcpu *v);
extern int cpu_get_interrupt(struct vcpu *v, int *type);
extern int cpu_has_pending_irq(struct vcpu *v);
-extern void hvm_release_assist_channel(struct vcpu *v);
// XXX - think about this, maybe use bit 30 of the mfn to signify an MMIO frame.
#define mmio_space(gpa) (!VALID_MFN(get_mfn_from_gpfn((gpa) >> PAGE_SHIFT)))
diff --git a/xen/include/asm-x86/hvm/support.h b/xen/include/asm-x86/hvm/support.h
index 1c557c228f..f9ab090dfe 100644
--- a/xen/include/asm-x86/hvm/support.h
+++ b/xen/include/asm-x86/hvm/support.h
@@ -32,8 +32,6 @@
#define HVM_DEBUG 1
#endif
-#define hvm_guest(v) ((v)->arch.guest_context.flags & VGCF_HVM_GUEST)
-
static inline shared_iopage_t *get_sp(struct domain *d)
{
return (shared_iopage_t *) d->arch.hvm_domain.shared_page_va;
@@ -96,13 +94,6 @@ enum hval_bitmaps {
#define VMX_DELIVER_NO_ERROR_CODE -1
-/*
- * This works for both 32bit & 64bit eflags filteration
- * done in construct_init_vmc[sb]_guest()
- */
-#define HVM_EFLAGS_RESERVED_0 0xffc08028 /* bitmap for 0 */
-#define HVM_EFLAGS_RESERVED_1 0x00000002 /* bitmap for 1 */
-
#if HVM_DEBUG
#define DBG_LEVEL_0 (1 << 0)
#define DBG_LEVEL_1 (1 << 1)
@@ -134,14 +125,16 @@ extern unsigned int opt_hvm_debug_level;
domain_crash_synchronous(); \
} while (0)
+#define TRACE_VMEXIT(index, value) \
+ current->arch.hvm_vcpu.hvm_trace_values[index] = (value)
+
extern int hvm_enabled;
-int hvm_copy_to_guest_phys(unsigned long paddr, void *buf, int size);
-int hvm_copy_from_guest_phys(void *buf, unsigned long paddr, int size);
+int hvm_copy_to_guest_phys(paddr_t paddr, void *buf, int size);
+int hvm_copy_from_guest_phys(void *buf, paddr_t paddr, int size);
int hvm_copy_to_guest_virt(unsigned long vaddr, void *buf, int size);
int hvm_copy_from_guest_virt(void *buf, unsigned long vaddr, int size);
-void hvm_setup_platform(struct domain* d);
void hvm_print_line(struct vcpu *v, const char c);
void hlt_timer_fn(void *data);
diff --git a/xen/include/asm-x86/hvm/svm/vmcb.h b/xen/include/asm-x86/hvm/svm/vmcb.h
index 5ac5de3a9a..2d6a4ed0f3 100644
--- a/xen/include/asm-x86/hvm/svm/vmcb.h
+++ b/xen/include/asm-x86/hvm/svm/vmcb.h
@@ -23,7 +23,7 @@
#include <asm/config.h>
#include <asm/hvm/hvm.h>
-extern int start_svm(void);
+int start_svm(void);
/* general 1 intercepts */
enum GenericIntercept1bits
@@ -310,10 +310,6 @@ enum {
SVM_CPU_STATE_LMA_ENABLED,
SVM_CPU_STATE_ASSIST_ENABLED,
};
-
-#define SVM_LONG_GUEST(ed) \
- (test_bit(SVM_CPU_STATE_LMA_ENABLED, &ed->arch.hvm_svm.cpu_state))
-
/*
* Attribute for segment selector. This is a copy of bit 40:47 & 52:55 of the
@@ -496,15 +492,15 @@ struct arch_svm_struct {
unsigned long cpu_state;
};
-extern struct vmcb_struct *alloc_vmcb(void);
-extern struct host_save_area *alloc_host_save_area(void);
-extern void free_vmcb(struct vmcb_struct *vmcb);
-extern void free_host_save_area(struct host_save_area *hsa);
+struct vmcb_struct *alloc_vmcb(void);
+struct host_save_area *alloc_host_save_area(void);
+void free_vmcb(struct vmcb_struct *vmcb);
+void free_host_save_area(struct host_save_area *hsa);
-extern int construct_vmcb(struct arch_svm_struct *, struct cpu_user_regs *);
-extern void destroy_vmcb(struct arch_svm_struct *);
+int svm_create_vmcb(struct vcpu *v);
+void svm_destroy_vmcb(struct vcpu *v);
-extern void setup_vmcb_dump(void);
+void setup_vmcb_dump(void);
#define VMCB_USE_HOST_ENV 1
#define VMCB_USE_SEPARATE_ENV 0
diff --git a/xen/include/asm-x86/hvm/vcpu.h b/xen/include/asm-x86/hvm/vcpu.h
index f613ae6a09..0c073028ee 100644
--- a/xen/include/asm-x86/hvm/vcpu.h
+++ b/xen/include/asm-x86/hvm/vcpu.h
@@ -32,7 +32,7 @@ struct hvm_vcpu {
unsigned long hw_cr3; /* value we give to HW to use */
unsigned long ioflags;
struct hvm_io_op io_op;
- struct vlapic *vlapic;
+ struct vlapic vlapic;
s64 cache_tsc_offset;
u64 guest_time;
@@ -44,8 +44,7 @@ struct hvm_vcpu {
/* Flags */
int flag_dr_dirty;
- /* hlt ins emulation wakeup timer */
- struct timer hlt_timer;
+ unsigned long hvm_trace_values[5];
union {
struct arch_vmx_struct vmx;
diff --git a/xen/include/asm-x86/hvm/vioapic.h b/xen/include/asm-x86/hvm/vioapic.h
index b5ec7e4597..32223341ef 100644
--- a/xen/include/asm-x86/hvm/vioapic.h
+++ b/xen/include/asm-x86/hvm/vioapic.h
@@ -23,99 +23,82 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#ifndef __ASM_X86_HVM_IOAPIC_H__
-#define __ASM_X86_HVM_IOAPIC_H__
+#ifndef __ASM_X86_HVM_VIOAPIC_H__
+#define __ASM_X86_HVM_VIOAPIC_H__
#include <xen/config.h>
#include <xen/types.h>
#include <xen/smp.h>
-#ifndef __ia64__
-#define IOAPIC_VERSION_ID 0x11
+#ifdef __ia64__
+#define VIOAPIC_IS_IOSAPIC 1
+#endif
+
+#if !VIOAPIC_IS_IOSAPIC
+#define VIOAPIC_VERSION_ID 0x11 /* IOAPIC version */
#else
-#define IOAPIC_VERSION_ID 0x21
+#define VIOAPIC_VERSION_ID 0x21 /* IOSAPIC version */
#endif
-#define IOAPIC_NUM_PINS 24
-#define MAX_LAPIC_NUM 32
+#define VIOAPIC_NUM_PINS 24
-#define IOAPIC_LEVEL_TRIGGER 1
+#define VIOAPIC_EDGE_TRIG 0
+#define VIOAPIC_LEVEL_TRIG 1
-#define IOAPIC_DEFAULT_BASE_ADDRESS 0xfec00000
-#define IOAPIC_MEM_LENGTH 0x100
+#define VIOAPIC_DEFAULT_BASE_ADDRESS 0xfec00000
+#define VIOAPIC_MEM_LENGTH 0x100
-#define IOAPIC_ENABLE_MASK 0x0
-#define IOAPIC_ENABLE_FLAG (1 << IOAPIC_ENABLE_MASK)
-#define IOAPICEnabled(s) (s->flags & IOAPIC_ENABLE_FLAG)
+/* Direct registers. */
+#define VIOAPIC_REG_SELECT 0x00
+#define VIOAPIC_REG_WINDOW 0x10
+#define VIOAPIC_REG_EOI 0x40 /* IA64 IOSAPIC only */
-#define IOAPIC_REG_SELECT 0x0
-#define IOAPIC_REG_WINDOW 0x10
+/* Indirect registers. */
+#define VIOAPIC_REG_APIC_ID 0x00 /* x86 IOAPIC only */
+#define VIOAPIC_REG_VERSION 0x01
+#define VIOAPIC_REG_ARB_ID 0x02 /* x86 IOAPIC only */
-#ifdef __ia64__
-#define IOAPIC_REG_ASSERTION 0x20
-#define IOAPIC_REG_EOI 0x40
-#endif
-
-#ifndef __ia64__
-#define IOAPIC_REG_APIC_ID 0x0
-#define IOAPIC_REG_ARB_ID 0x2
-#endif
+#define domain_vioapic(d) (&(d)->arch.hvm_domain.vioapic)
+#define vioapic_domain(v) (container_of((v), struct domain, \
+ arch.hvm_domain.vioapic))
-#define IOAPIC_REG_VERSION 0x1
-
-typedef union RedirStatus
+union vioapic_redir_entry
{
- uint64_t value;
+ uint64_t bits;
struct {
uint8_t vector;
- uint8_t deliver_mode:3;
- uint8_t destmode:1;
- uint8_t delivestatus:1;
+ uint8_t delivery_mode:3;
+ uint8_t dest_mode:1;
+ uint8_t delivery_status:1;
uint8_t polarity:1;
- uint8_t remoteirr:1;
- uint8_t trigmod:1;
- uint8_t mask:1; /* interrupt mask*/
+ uint8_t remote_irr:1;
+ uint8_t trig_mode:1;
+ uint8_t mask:1;
uint8_t reserve:7;
-#ifndef __ia64__
+#if !VIOAPIC_IS_IOSAPIC
uint8_t reserved[4];
uint8_t dest_id;
#else
uint8_t reserved[3];
uint16_t dest_id;
#endif
- } RedirForm;
-} RedirStatus;
+ } fields;
+};
-typedef struct hvm_vioapic {
+struct vioapic {
uint32_t irr;
uint32_t irr_xen; /* interrupts forced on by the hypervisor. */
- uint32_t isr; /* This is used for level trigger */
+ uint32_t isr; /* This is used for level trigger */
uint32_t imr;
uint32_t ioregsel;
- uint32_t flags;
- uint32_t lapic_count;
uint32_t id;
- uint32_t arb_id;
unsigned long base_address;
- RedirStatus redirtbl[IOAPIC_NUM_PINS];
- struct vlapic *lapic_info[MAX_LAPIC_NUM];
- struct domain *domain;
-} hvm_vioapic_t;
-
-hvm_vioapic_t *hvm_vioapic_init(struct domain *d);
-
-void hvm_vioapic_do_irqs_clear(struct domain *d, uint16_t irqs);
-void hvm_vioapic_do_irqs(struct domain *d, uint16_t irqs);
-void hvm_vioapic_set_xen_irq(struct domain *d, int irq, int level);
-void hvm_vioapic_set_irq(struct domain *d, int irq, int level);
+ union vioapic_redir_entry redirtbl[VIOAPIC_NUM_PINS];
+};
-int hvm_vioapic_add_lapic(struct vlapic *vlapic, struct vcpu *v);
-
-void ioapic_update_EOI(struct domain *d, int vector);
-
-#ifdef HVM_DOMAIN_SAVE_RESTORE
-void ioapic_save(QEMUFile* f, void* opaque);
-int ioapic_load(QEMUFile* f, void* opaque, int version_id);
-#endif
+void vioapic_init(struct domain *d);
+void vioapic_set_xen_irq(struct domain *d, int irq, int level);
+void vioapic_set_irq(struct domain *d, int irq, int level);
+void vioapic_update_EOI(struct domain *d, int vector);
-#endif /* __ASM_X86_HVM_IOAPIC_H__ */
+#endif /* __ASM_X86_HVM_VIOAPIC_H__ */
diff --git a/xen/include/asm-x86/hvm/vlapic.h b/xen/include/asm-x86/hvm/vlapic.h
index 1da56bd0ca..c327f5ef3f 100644
--- a/xen/include/asm-x86/hvm/vlapic.h
+++ b/xen/include/asm-x86/hvm/vlapic.h
@@ -25,56 +25,14 @@
#define MAX_VECTOR 256
-#define VEC_POS(v) ((v)%32)
-#define REG_POS(v) (((v)/32)* 0x10)
-#define vlapic_test_and_set_vector(vec, bitmap) \
- test_and_set_bit(VEC_POS(vec), (bitmap) + REG_POS(vec))
-#define vlapic_test_and_clear_vector(vec, bitmap) \
- test_and_clear_bit(VEC_POS(vec), (bitmap) + REG_POS(vec))
-#define vlapic_set_vector(vec, bitmap) \
- set_bit(VEC_POS(vec), (bitmap) + REG_POS(vec))
-#define vlapic_clear_vector(vec, bitmap) \
- clear_bit(VEC_POS(vec), (bitmap) + REG_POS(vec))
-
-static inline int vlapic_find_highest_vector(u32 *bitmap)
-{
- int word_offset = MAX_VECTOR / 32;
-
- /* Work backwards through the bitmap (first 32-bit word in every four). */
- while ( (word_offset != 0) && (bitmap[(--word_offset)*4] == 0) )
- continue;
-
- return (fls(bitmap[word_offset*4]) - 1) + (word_offset * 32);
-}
-
-#define VLAPIC(v) (v->arch.hvm_vcpu.vlapic)
-
-#define VLAPIC_VERSION 0x00050014
-
-#define VLOCAL_APIC_MEM_LENGTH (1 << 12)
-
-#define VLAPIC_LVT_NUM 6
+#define vcpu_vlapic(vcpu) (&(vcpu)->arch.hvm_vcpu.vlapic)
+#define vlapic_vcpu(vpic) (container_of((vpic), struct vcpu, \
+ arch.hvm_vcpu.vlapic))
+#define vlapic_domain(vpic) (vlapic_vcpu(vlapic)->domain)
#define VLAPIC_ID(vlapic) \
(GET_APIC_ID(vlapic_get_reg(vlapic, APIC_ID)))
-/* followed define is not in apicdef.h */
-#define APIC_SHORT_MASK 0xc0000
-#define APIC_DEST_NOSHORT 0x0
-#define APIC_DEST_MASK 0x800
-
-#define vlapic_lvt_enabled(vlapic, lvt_type) \
- (!(vlapic_get_reg(vlapic, lvt_type) & APIC_LVT_MASKED))
-
-#define vlapic_lvt_vector(vlapic, lvt_type) \
- (vlapic_get_reg(vlapic, lvt_type) & APIC_VECTOR_MASK)
-
-#define vlapic_lvt_dm(vlapic, lvt_type) \
- (vlapic_get_reg(vlapic, lvt_type) & APIC_MODE_MASK)
-
-#define vlapic_lvtt_period(vlapic) \
- (vlapic_get_reg(vlapic, APIC_LVTT) & APIC_LVT_TIMER_PERIODIC)
-
#define _VLAPIC_GLOB_DISABLE 0x0
#define VLAPIC_GLOB_DISABLE_MASK 0x1
#define VLAPIC_SOFTWARE_DISABLE_MASK 0x2
@@ -87,79 +45,49 @@ static inline int vlapic_find_highest_vector(u32 *bitmap)
#define vlapic_global_enabled(vlapic) \
(!(test_bit(_VLAPIC_GLOB_DISABLE, &(vlapic)->status)))
-#define LVT_MASK \
- APIC_LVT_MASKED | APIC_SEND_PENDING | APIC_VECTOR_MASK
-
-#define LINT_MASK \
- LVT_MASK | APIC_MODE_MASK | APIC_INPUT_POLARITY |\
- APIC_LVT_REMOTE_IRR | APIC_LVT_LEVEL_TRIGGER
-
-typedef struct direct_intr_info {
- int deliver_mode;
- int source[6];
-} direct_intr_info_t;
-
struct vlapic {
uint32_t status;
- uint32_t vcpu_id;
uint64_t apic_base_msr;
unsigned long base_address;
- uint32_t timer_divide_count;
+ uint32_t timer_divisor;
struct timer vlapic_timer;
- int intr_pending_count[MAX_VECTOR];
+ int timer_pending_count;
+ int flush_tpr_threshold;
s_time_t timer_last_update;
- direct_intr_info_t direct_intr;
- uint32_t err_status;
- uint32_t err_write_count;
- struct vcpu *vcpu;
- struct domain *domain;
struct page_info *regs_page;
void *regs;
};
-static inline int vlapic_set_irq(struct vlapic *vlapic,
- uint8_t vec, uint8_t trig)
-{
- int ret;
-
- ret = vlapic_test_and_set_vector(vec, vlapic->regs + APIC_IRR);
- if ( trig )
- vlapic_set_vector(vec, vlapic->regs + APIC_TMR);
-
- /* We may need to wake up target vcpu, besides set pending bit here */
- return ret;
-}
-
static inline uint32_t vlapic_get_reg(struct vlapic *vlapic, uint32_t reg)
{
- return *( (uint32_t *)(vlapic->regs + reg));
+ return *((uint32_t *)(vlapic->regs + reg));
}
-static inline void vlapic_set_reg(struct vlapic *vlapic,
- uint32_t reg, uint32_t val)
+static inline void vlapic_set_reg(
+ struct vlapic *vlapic, uint32_t reg, uint32_t val)
{
*((uint32_t *)(vlapic->regs + reg)) = val;
}
-void vlapic_post_injection(struct vcpu* v, int vector, int deliver_mode);
+int vlapic_set_irq(struct vlapic *vlapic, uint8_t vec, uint8_t trig);
-int cpu_has_apic_interrupt(struct vcpu* v);
-int cpu_get_apic_interrupt(struct vcpu* v, int *mode);
+void vlapic_post_injection(struct vcpu *v, int vector, int deliver_mode);
-extern int vlapic_init(struct vcpu *vc);
+int vlapic_find_highest_irr(struct vlapic *vlapic);
-extern void vlapic_msr_set(struct vlapic *vlapic, uint64_t value);
+int cpu_get_apic_interrupt(struct vcpu *v, int *mode);
+
+int vlapic_init(struct vcpu *v);
+void vlapic_destroy(struct vcpu *v);
+
+void vlapic_msr_set(struct vlapic *vlapic, uint64_t value);
int vlapic_accept_pic_intr(struct vcpu *v);
-struct vlapic* apic_round_robin(struct domain *d,
- uint8_t dest_mode,
- uint8_t vector,
- uint32_t bitmap);
+struct vlapic *apic_round_robin(
+ struct domain *d, uint8_t vector, uint32_t bitmap);
s_time_t get_apictime_scheduled(struct vcpu *v);
-int hvm_apic_support(struct domain *d);
-
#endif /* __ASM_X86_HVM_VLAPIC_H__ */
diff --git a/xen/include/asm-x86/hvm/vmx/vmcs.h b/xen/include/asm-x86/hvm/vmx/vmcs.h
index 868c6fd3cb..e1a6e5a393 100644
--- a/xen/include/asm-x86/hvm/vmx/vmcs.h
+++ b/xen/include/asm-x86/hvm/vmx/vmcs.h
@@ -50,10 +50,6 @@ struct vmx_msr_state {
unsigned long shadow_gs;
};
-/* io bitmap is 4KBytes in size */
-#define IO_BITMAP_SIZE 0x1000
-#define IO_BITMAP_ORDER (get_order_from_bytes(IO_BITMAP_SIZE))
-
struct arch_vmx_struct {
/* Virtual address of VMCS. */
struct vmcs_struct *vmcs;
@@ -80,9 +76,7 @@ struct arch_vmx_struct {
unsigned long cpu_shadow_cr4; /* copy of guest read shadow CR4 */
unsigned long cpu_cr2; /* save CR2 */
unsigned long cpu_cr3;
- unsigned long cpu_based_exec_control;
struct vmx_msr_state msr_content;
- void *io_bitmap_a, *io_bitmap_b;
unsigned long vmxassist_enabled:1;
};
diff --git a/xen/include/asm-x86/hvm/vmx/vmx.h b/xen/include/asm-x86/hvm/vmx/vmx.h
index 4a2051e5bd..eca3b683ed 100644
--- a/xen/include/asm-x86/hvm/vmx/vmx.h
+++ b/xen/include/asm-x86/hvm/vmx/vmx.h
@@ -30,7 +30,6 @@ extern void vmx_asm_vmexit_handler(struct cpu_user_regs);
extern void vmx_asm_do_vmentry(void);
extern void vmx_intr_assist(void);
extern void vmx_migrate_timers(struct vcpu *v);
-extern void arch_vmx_do_launch(struct vcpu *);
extern void arch_vmx_do_resume(struct vcpu *);
extern void set_guest_time(struct vcpu *v, u64 gtime);
@@ -94,6 +93,7 @@ extern unsigned int cpu_rev;
#define INTR_INFO_VALID_MASK 0x80000000 /* 31 */
#define INTR_TYPE_EXT_INTR (0 << 8) /* external interrupt */
+#define INTR_TYPE_NMI (2 << 8) /* NMI */
#define INTR_TYPE_HW_EXCEPTION (3 << 8) /* hardware exception */
#define INTR_TYPE_SW_EXCEPTION (6 << 8) /* software exception */
@@ -183,135 +183,55 @@ static inline void __vmpclear(u64 addr)
: "memory");
}
-#define __vmread(x, ptr) ___vmread((x), (ptr), sizeof(*(ptr)))
-
-static always_inline int ___vmread(
- const unsigned long field, void *ptr, const int size)
+static inline unsigned long __vmread(unsigned long field)
{
- unsigned long ecx = 0;
- int rc;
+ unsigned long ecx;
__asm__ __volatile__ ( VMREAD_OPCODE
MODRM_EAX_ECX
- /* CF==1 or ZF==1 --> rc = -1 */
- "setna %b0 ; neg %0"
- : "=q" (rc), "=c" (ecx)
- : "0" (0), "a" (field)
+ /* CF==1 or ZF==1 --> crash (ud2) */
+ "ja 1f ; ud2 ; 1:\n"
+ : "=c" (ecx)
+ : "a" (field)
: "memory");
- switch ( size ) {
- case 1:
- *((u8 *) (ptr)) = ecx;
- break;
- case 2:
- *((u16 *) (ptr)) = ecx;
- break;
- case 4:
- *((u32 *) (ptr)) = ecx;
- break;
- case 8:
- *((u64 *) (ptr)) = ecx;
- break;
- default:
- domain_crash_synchronous();
- break;
- }
-
- return rc;
+ return ecx;
}
-
-static always_inline void __vmwrite_vcpu(
- struct vcpu *v, unsigned long field, unsigned long value)
-{
- switch ( field ) {
- case CR0_READ_SHADOW:
- v->arch.hvm_vmx.cpu_shadow_cr0 = value;
- break;
- case GUEST_CR0:
- v->arch.hvm_vmx.cpu_cr0 = value;
- break;
- case CR4_READ_SHADOW:
- v->arch.hvm_vmx.cpu_shadow_cr4 = value;
- break;
- case CPU_BASED_VM_EXEC_CONTROL:
- v->arch.hvm_vmx.cpu_based_exec_control = value;
- break;
- default:
- printk("__vmwrite_cpu: invalid field %lx\n", field);
- break;
- }
-}
-
-static always_inline void __vmread_vcpu(
- struct vcpu *v, unsigned long field, unsigned long *value)
+static inline void __vmwrite(unsigned long field, unsigned long value)
{
- switch ( field ) {
- case CR0_READ_SHADOW:
- *value = v->arch.hvm_vmx.cpu_shadow_cr0;
- break;
- case GUEST_CR0:
- *value = v->arch.hvm_vmx.cpu_cr0;
- break;
- case CR4_READ_SHADOW:
- *value = v->arch.hvm_vmx.cpu_shadow_cr4;
- break;
- case CPU_BASED_VM_EXEC_CONTROL:
- *value = v->arch.hvm_vmx.cpu_based_exec_control;
- break;
- default:
- printk("__vmread_vcpu: invalid field %lx\n", field);
- break;
- }
+ __asm__ __volatile__ ( VMWRITE_OPCODE
+ MODRM_EAX_ECX
+ /* CF==1 or ZF==1 --> crash (ud2) */
+ "ja 1f ; ud2 ; 1:\n"
+ :
+ : "a" (field) , "c" (value)
+ : "memory");
}
-static inline int __vmwrite(unsigned long field, unsigned long value)
+static inline unsigned long __vmread_safe(unsigned long field, int *error)
{
- struct vcpu *v = current;
- int rc;
+ unsigned long ecx;
- __asm__ __volatile__ ( VMWRITE_OPCODE
+ __asm__ __volatile__ ( VMREAD_OPCODE
MODRM_EAX_ECX
/* CF==1 or ZF==1 --> rc = -1 */
"setna %b0 ; neg %0"
- : "=q" (rc)
- : "0" (0), "a" (field) , "c" (value)
+ : "=q" (*error), "=c" (ecx)
+ : "0" (0), "a" (field)
: "memory");
- switch ( field ) {
- case CR0_READ_SHADOW:
- case GUEST_CR0:
- case CR4_READ_SHADOW:
- case CPU_BASED_VM_EXEC_CONTROL:
- __vmwrite_vcpu(v, field, value);
- break;
- }
-
- return rc;
+ return ecx;
}
-static inline int __vm_set_bit(unsigned long field, unsigned long mask)
+static inline void __vm_set_bit(unsigned long field, unsigned long mask)
{
- unsigned long tmp;
- int err = 0;
-
- err |= __vmread(field, &tmp);
- tmp |= mask;
- err |= __vmwrite(field, tmp);
-
- return err;
+ __vmwrite(field, __vmread(field) | mask);
}
-static inline int __vm_clear_bit(unsigned long field, unsigned long mask)
+static inline void __vm_clear_bit(unsigned long field, unsigned long mask)
{
- unsigned long tmp;
- int err = 0;
-
- err |= __vmread(field, &tmp);
- tmp &= ~mask;
- err |= __vmwrite(field, tmp);
-
- return err;
+ __vmwrite(field, __vmread(field) & ~mask);
}
static inline void __vmxoff (void)
@@ -337,18 +257,10 @@ static inline int __vmxon (u64 addr)
static inline int vmx_paging_enabled(struct vcpu *v)
{
- unsigned long cr0;
- __vmread_vcpu(v, CR0_READ_SHADOW, &cr0);
+ unsigned long cr0 = v->arch.hvm_vmx.cpu_shadow_cr0;
return ((cr0 & (X86_CR0_PE|X86_CR0_PG)) == (X86_CR0_PE|X86_CR0_PG));
}
-static inline int vmx_pae_enabled(struct vcpu *v)
-{
- unsigned long cr4;
- __vmread_vcpu(v, CR4_READ_SHADOW, &cr4);
- return (vmx_paging_enabled(v) && (cr4 & X86_CR4_PAE));
-}
-
static inline int vmx_long_mode_enabled(struct vcpu *v)
{
u64 efer = v->arch.hvm_vmx.msr_content.msr_items[VMX_INDEX_MSR_EFER];
@@ -370,9 +282,7 @@ static inline void vmx_update_host_cr3(struct vcpu *v)
static inline int vmx_pgbit_test(struct vcpu *v)
{
- unsigned long cr0;
-
- __vmread_vcpu(v, CR0_READ_SHADOW, &cr0);
+ unsigned long cr0 = v->arch.hvm_vmx.cpu_shadow_cr0;
return (cr0 & X86_CR0_PG);
}
diff --git a/xen/include/asm-x86/hvm/vpic.h b/xen/include/asm-x86/hvm/vpic.h
index 8e2d386e59..49c2df9011 100644
--- a/xen/include/asm-x86/hvm/vpic.h
+++ b/xen/include/asm-x86/hvm/vpic.h
@@ -5,10 +5,10 @@
* Copyright (c) 2005 Intel Corp
*
* 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
+ * 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
@@ -18,19 +18,17 @@
* 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.
+ * 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 __ASM_X86_HVM_VPIC_H__
#define __ASM_X86_HVM_VPIC_H__
-#define hw_error(x) do {} while (0);
+#define domain_vpic(d) (&(d)->arch.hvm_domain.vpic)
+#define vpic_domain(v) (container_of((v), struct domain, arch.hvm_domain.vpic))
-
-/* i8259.c */
-typedef struct IOAPICState IOAPICState;
typedef struct PicState {
uint8_t last_irr; /* edge detection */
uint8_t irr; /* interrupt request register */
@@ -50,12 +48,11 @@ typedef struct PicState {
uint8_t init4; /* true if 4 byte init */
uint8_t elcr; /* PIIX edge/trigger selection*/
uint8_t elcr_mask;
- struct hvm_virpic *pics_state;
+ struct vpic *pics_state;
} PicState;
-struct hvm_virpic {
+struct vpic {
/* 0 is master pic, 1 is slave pic */
- /* XXX: better separation between the two pics */
PicState pics[2];
void (*irq_request)(void *opaque, int level);
void *irq_request_opaque;
@@ -63,21 +60,15 @@ struct hvm_virpic {
spinlock_t lock;
};
-
void pic_set_xen_irq(void *opaque, int irq, int level);
-void pic_set_irq(struct hvm_virpic *s, int irq, int level);
-void pic_set_irq_new(void *opaque, int irq, int level);
-void pic_init(struct hvm_virpic *s,
+void pic_set_irq(struct vpic *vpic, int irq, int level);
+void pic_init(struct vpic *vpic,
void (*irq_request)(void *, int),
void *irq_request_opaque);
-int pic_read_irq(struct hvm_virpic *s);
-void pic_update_irq(struct hvm_virpic *s); /* Caller must hold s->lock */
-uint32_t pic_intack_read(struct hvm_virpic *s);
-void register_pic_io_hook (void);
+void pic_update_irq(struct vpic *vpic); /* Caller must hold vpic->lock */
+void register_pic_io_hook(struct domain *d);
int cpu_get_pic_interrupt(struct vcpu *v, int *type);
int is_periodic_irq(struct vcpu *v, int irq, int type);
int is_irq_enabled(struct vcpu *v, int irq);
-void do_pic_irqs (struct hvm_virpic *s, uint16_t irqs);
-void do_pic_irqs_clear (struct hvm_virpic *s, uint16_t irqs);
#endif /* __ASM_X86_HVM_VPIC_H__ */
diff --git a/xen/include/asm-x86/hvm/vpt.h b/xen/include/asm-x86/hvm/vpt.h
index ada8936af7..b4492e1d59 100644
--- a/xen/include/asm-x86/hvm/vpt.h
+++ b/xen/include/asm-x86/hvm/vpt.h
@@ -91,6 +91,7 @@ struct periodic_time {
char one_shot; /* one shot time */
char irq;
char first_injected; /* flag to prevent shadow window */
+ u32 bind_vcpu; /* vcpu timer interrupt delivers to */
u32 pending_intr_nr; /* the couner for pending timer interrupts */
u32 period; /* frequency in ns */
u64 period_cycles; /* frequency in cpu cycles */
diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h
index 389366a7ba..cb7dfd7ebb 100644
--- a/xen/include/asm-x86/mm.h
+++ b/xen/include/asm-x86/mm.h
@@ -179,8 +179,8 @@ void init_frametable(void);
int alloc_page_type(struct page_info *page, unsigned long type);
void free_page_type(struct page_info *page, unsigned long type);
-extern void invalidate_shadow_ldt(struct vcpu *d);
-extern int _shadow_mode_refcounts(struct domain *d);
+void invalidate_shadow_ldt(struct vcpu *d);
+int _shadow_mode_refcounts(struct domain *d);
static inline void put_page(struct page_info *page)
{
@@ -213,7 +213,8 @@ static inline int get_page(struct page_info *page,
unlikely(d != _domain) ) /* Wrong owner? */
{
if ( !_shadow_mode_refcounts(domain) )
- DPRINTK("Error pfn %lx: rd=%p, od=%p, caf=%08x, taf=%"
+ gdprintk(XENLOG_INFO,
+ "Error pfn %lx: rd=%p, od=%p, caf=%08x, taf=%"
PRtype_info "\n",
page_to_mfn(page), domain, unpickle_domptr(d),
x, page->u.inuse.type_info);
@@ -384,4 +385,6 @@ long subarch_memory_op(int op, XEN_GUEST_HANDLE(void) arg);
int steal_page(
struct domain *d, struct page_info *page, unsigned int memflags);
+int map_ldt_shadow_page(unsigned int);
+
#endif /* __ASM_X86_MM_H__ */
diff --git a/xen/include/asm-x86/page.h b/xen/include/asm-x86/page.h
index 905d76d576..b9c921a5cd 100644
--- a/xen/include/asm-x86/page.h
+++ b/xen/include/asm-x86/page.h
@@ -25,6 +25,18 @@
# include <asm/x86_64/page.h>
#endif
+/* Read a pte atomically from memory. */
+#define l1e_read_atomic(l1ep) l1e_from_intpte(pte_read_atomic(l1ep))
+#define l2e_read_atomic(l2ep) l2e_from_intpte(pte_read_atomic(l2ep))
+#define l3e_read_atomic(l3ep) l3e_from_intpte(pte_read_atomic(l3ep))
+#define l4e_read_atomic(l4ep) l4e_from_intpte(pte_read_atomic(l4ep))
+
+/* Write a pte atomically to memory. */
+#define l1e_write_atomic(l1ep, l1e) pte_write_atomic(l1ep, l1e_get_intpte(l1e))
+#define l2e_write_atomic(l2ep, l2e) pte_write_atomic(l2ep, l1e_get_intpte(l2e))
+#define l3e_write_atomic(l3ep, l3e) pte_write_atomic(l3ep, l1e_get_intpte(l3e))
+#define l4e_write_atomic(l4ep, l4e) pte_write_atomic(l4ep, l1e_get_intpte(l4e))
+
/* Get direct integer representation of a pte's contents (intpte_t). */
#define l1e_get_intpte(x) ((x).l1)
#define l2e_get_intpte(x) ((x).l2)
diff --git a/xen/include/asm-x86/perfc_defn.h b/xen/include/asm-x86/perfc_defn.h
index 7204348fca..ba1e32c8fd 100644
--- a/xen/include/asm-x86/perfc_defn.h
+++ b/xen/include/asm-x86/perfc_defn.h
@@ -43,6 +43,9 @@ PERFCOUNTER_CPU(shadow_linear_map_failed, "shadow hit read-only linear map")
PERFCOUNTER_CPU(shadow_a_update, "shadow A bit update")
PERFCOUNTER_CPU(shadow_ad_update, "shadow A&D bit update")
PERFCOUNTER_CPU(shadow_fault, "calls to shadow_fault")
+PERFCOUNTER_CPU(shadow_fault_fast_gnp, "shadow_fault fast path n/p")
+PERFCOUNTER_CPU(shadow_fault_fast_mmio, "shadow_fault fast path mmio")
+PERFCOUNTER_CPU(shadow_fault_fast_fail, "shadow_fault fast path error")
PERFCOUNTER_CPU(shadow_fault_bail_bad_gfn, "shadow_fault guest bad gfn")
PERFCOUNTER_CPU(shadow_fault_bail_not_present,
"shadow_fault guest not-present")
diff --git a/xen/include/asm-x86/processor.h b/xen/include/asm-x86/processor.h
index 09ecfab041..7a18e0d3d7 100644
--- a/xen/include/asm-x86/processor.h
+++ b/xen/include/asm-x86/processor.h
@@ -107,7 +107,7 @@
#define TRAP_deferred_nmi 31
/* Set for entry via SYSCALL. Informs return code to use SYSRETQ not IRETQ. */
-/* NB. Same as VGCF_IN_SYSCALL. No bits in common with any other TRAP_ defn. */
+/* NB. Same as VGCF_in_syscall. No bits in common with any other TRAP_ defn. */
#define TRAP_syscall 256
/*
diff --git a/xen/include/asm-x86/regs.h b/xen/include/asm-x86/regs.h
index 8ba10c4722..48f8b86c85 100644
--- a/xen/include/asm-x86/regs.h
+++ b/xen/include/asm-x86/regs.h
@@ -39,7 +39,7 @@ enum EFLAGS {
/* If a guest frame, it must be have guest privs (unless HVM guest). */ \
/* We permit CS==0 which can come from an uninitialised trap entry. */ \
ASSERT((diff != 0) || vm86_mode(r) || ((r->cs&3) >= GUEST_KERNEL_RPL) || \
- (r->cs == 0) || hvm_guest(current)); \
+ (r->cs == 0) || is_hvm_vcpu(current)); \
/* If not a guest frame, it must be a hypervisor frame. */ \
ASSERT((diff == 0) || (!vm86_mode(r) && (r->cs == __HYPERVISOR_CS))); \
/* Return TRUE if it's a guest frame. */ \
diff --git a/xen/include/asm-x86/shadow.h b/xen/include/asm-x86/shadow.h
index 27cf6843eb..dfc1122cb5 100644
--- a/xen/include/asm-x86/shadow.h
+++ b/xen/include/asm-x86/shadow.h
@@ -23,7 +23,7 @@
#ifndef _XEN_SHADOW_H
#define _XEN_SHADOW_H
-#include <public/domctl.h>
+#include <public/domctl.h>
#include <xen/sched.h>
#include <xen/perfc.h>
#include <xen/domain_page.h>
@@ -64,7 +64,7 @@
#define shadow_mode_external(_d) ((_d)->arch.shadow.mode & SHM2_external)
/* Xen traps & emulates all reads of all page table pages:
- *not yet supported
+ * not yet supported
*/
#define shadow_mode_trap_reads(_d) ({ (void)(_d); 0; })
@@ -77,7 +77,7 @@
#ifdef __x86_64__
#define pv_32bit_guest(_v) 0 // not yet supported
#else
-#define pv_32bit_guest(_v) !hvm_guest(v)
+#define pv_32bit_guest(_v) !is_hvm_vcpu(v)
#endif
/* The shadow lock.
@@ -103,36 +103,36 @@
#error shadow.h currently requires CONFIG_SMP
#endif
-#define shadow_lock_init(_d) \
- do { \
- spin_lock_init(&(_d)->arch.shadow.lock); \
- (_d)->arch.shadow.locker = -1; \
- (_d)->arch.shadow.locker_function = "nobody"; \
+#define shadow_lock_init(_d) \
+ do { \
+ spin_lock_init(&(_d)->arch.shadow.lock); \
+ (_d)->arch.shadow.locker = -1; \
+ (_d)->arch.shadow.locker_function = "nobody"; \
} while (0)
-#define shadow_lock_is_acquired(_d) \
+#define shadow_lock_is_acquired(_d) \
(current->processor == (_d)->arch.shadow.locker)
#define shadow_lock(_d) \
- do { \
+ do { \
if ( unlikely((_d)->arch.shadow.locker == current->processor) ) \
- { \
+ { \
printk("Error: shadow lock held by %s\n", \
(_d)->arch.shadow.locker_function); \
- BUG(); \
- } \
+ BUG(); \
+ } \
spin_lock(&(_d)->arch.shadow.lock); \
ASSERT((_d)->arch.shadow.locker == -1); \
(_d)->arch.shadow.locker = current->processor; \
(_d)->arch.shadow.locker_function = __func__; \
} while (0)
-#define shadow_unlock(_d) \
- do { \
- ASSERT((_d)->arch.shadow.locker == current->processor); \
- (_d)->arch.shadow.locker = -1; \
- (_d)->arch.shadow.locker_function = "nobody"; \
- spin_unlock(&(_d)->arch.shadow.lock); \
+#define shadow_unlock(_d) \
+ do { \
+ ASSERT((_d)->arch.shadow.locker == current->processor); \
+ (_d)->arch.shadow.locker = -1; \
+ (_d)->arch.shadow.locker_function = "nobody"; \
+ spin_unlock(&(_d)->arch.shadow.lock); \
} while (0)
/*
@@ -161,8 +161,10 @@ extern int shadow_audit_enable;
*/
#define SHOPT_WRITABLE_HEURISTIC 0x01 /* Guess at RW PTEs via linear maps */
#define SHOPT_EARLY_UNSHADOW 0x02 /* Unshadow l1s on fork or exit */
+#define SHOPT_FAST_FAULT_PATH 0x04 /* Fast-path MMIO and not-present */
+#define SHOPT_PREFETCH 0x08 /* Shadow multiple entries per fault */
-#define SHADOW_OPTIMIZATIONS 0x03
+#define SHADOW_OPTIMIZATIONS 0x0f
/* With shadow pagetables, the different kinds of address start
@@ -259,7 +261,7 @@ struct shadow_paging_mode {
int (*page_fault )(struct vcpu *v, unsigned long va,
struct cpu_user_regs *regs);
int (*invlpg )(struct vcpu *v, unsigned long va);
- unsigned long (*gva_to_gpa )(struct vcpu *v, unsigned long va);
+ paddr_t (*gva_to_gpa )(struct vcpu *v, unsigned long va);
unsigned long (*gva_to_gfn )(struct vcpu *v, unsigned long va);
void (*update_cr3 )(struct vcpu *v);
int (*map_and_validate_gl1e )(struct vcpu *v, mfn_t gmfn,
@@ -311,6 +313,9 @@ static inline int shadow_guest_paging_levels(struct vcpu *v)
/**************************************************************************/
/* Entry points into the shadow code */
+/* Enable arbitrary shadow mode. */
+int shadow_enable(struct domain *d, u32 mode);
+
/* Turning on shadow test mode */
int shadow_test_enable(struct domain *d);
@@ -368,11 +373,13 @@ shadow_invlpg(struct vcpu *v, unsigned long va)
return v->arch.shadow.mode->invlpg(v, va);
}
-static inline unsigned long
+static inline paddr_t
shadow_gva_to_gpa(struct vcpu *v, unsigned long va)
/* Called to translate a guest virtual address to what the *guest*
* pagetables would map it to. */
{
+ if ( unlikely(!shadow_vcpu_mode_translate(v)) )
+ return (paddr_t) va;
return v->arch.shadow.mode->gva_to_gpa(v, va);
}
@@ -381,6 +388,8 @@ shadow_gva_to_gfn(struct vcpu *v, unsigned long va)
/* Called to translate a guest virtual address to what the *guest*
* pagetables would map it to. */
{
+ if ( unlikely(!shadow_vcpu_mode_translate(v)) )
+ return va >> PAGE_SHIFT;
return v->arch.shadow.mode->gva_to_gfn(v, va);
}
@@ -673,21 +682,6 @@ sh_gfn_to_mfn(struct domain *d, unsigned long gfn)
return sh_gfn_to_mfn_foreign(d, gfn);
}
-// vcpu-specific version of gfn_to_mfn(). This is where we hide the dirty
-// little secret that, for hvm guests with paging disabled, nearly all of the
-// shadow code actually think that the guest is running on *untranslated* page
-// tables (which is actually domain->phys_table).
-//
-static inline mfn_t
-sh_vcpu_gfn_to_mfn(struct vcpu *v, unsigned long gfn)
-{
- if ( !shadow_vcpu_mode_translate(v) )
- return _mfn(gfn);
- if ( likely(current->domain == v->domain) )
- return _mfn(get_mfn_from_gpfn(gfn));
- return sh_gfn_to_mfn_foreign(v->domain, gfn);
-}
-
static inline unsigned long
sh_mfn_to_gfn(struct domain *d, mfn_t mfn)
{
diff --git a/xen/include/asm-x86/x86_32/page-2level.h b/xen/include/asm-x86/x86_32/page-2level.h
index e7b74dd18d..a022b12087 100644
--- a/xen/include/asm-x86/x86_32/page-2level.h
+++ b/xen/include/asm-x86/x86_32/page-2level.h
@@ -28,6 +28,9 @@ typedef l2_pgentry_t root_pgentry_t;
#endif /* !__ASSEMBLY__ */
+#define pte_read_atomic(ptep) (*(intpte_t *)(ptep))
+#define pte_write_atomic(ptep, pte) (*(intpte_t *)(ptep)) = (pte))
+
/* root table */
#define root_get_pfn l2e_get_pfn
#define root_get_flags l2e_get_flags
diff --git a/xen/include/asm-x86/x86_32/page-3level.h b/xen/include/asm-x86/x86_32/page-3level.h
index 8450c9c194..08864255ad 100644
--- a/xen/include/asm-x86/x86_32/page-3level.h
+++ b/xen/include/asm-x86/x86_32/page-3level.h
@@ -38,6 +38,17 @@ typedef l3_pgentry_t root_pgentry_t;
#endif /* !__ASSEMBLY__ */
+#define pte_read_atomic(ptep) ({ \
+ intpte_t __pte = *(intpte_t *)(ptep), __npte; \
+ while ( (__npte = cmpxchg((intpte_t *)(ptep), __pte, __pte)) != __pte ) \
+ __pte = __npte; \
+ __pte; })
+#define pte_write_atomic(ptep, pte) do { \
+ intpte_t __pte = *(intpte_t *)(ptep), __npte; \
+ while ( (__npte = cmpxchg((intpte_t *)(ptep), __pte, (pte))) != __pte ) \
+ __pte = __npte; \
+} while ( 0 )
+
/* root table */
#define root_get_pfn l3e_get_pfn
#define root_get_flags l3e_get_flags
diff --git a/xen/include/asm-x86/x86_64/page.h b/xen/include/asm-x86/x86_64/page.h
index 05c8c57baa..4e0f3675ce 100644
--- a/xen/include/asm-x86/x86_64/page.h
+++ b/xen/include/asm-x86/x86_64/page.h
@@ -41,6 +41,9 @@ typedef l4_pgentry_t root_pgentry_t;
#endif /* !__ASSEMBLY__ */
+#define pte_read_atomic(ptep) (*(intpte_t *)(ptep))
+#define pte_write_atomic(ptep, pte) (*(intpte_t *)(ptep)) = (pte))
+
/* Given a virtual address, get an entry offset into a linear page table. */
#define l1_linear_offset(_a) (((_a) & VADDR_MASK) >> L1_PAGETABLE_SHIFT)
#define l2_linear_offset(_a) (((_a) & VADDR_MASK) >> L2_PAGETABLE_SHIFT)
diff --git a/xen/include/public/COPYING b/xen/include/public/COPYING
index 3377710a5c..ffc6d6166f 100644
--- a/xen/include/public/COPYING
+++ b/xen/include/public/COPYING
@@ -1,9 +1,19 @@
XEN NOTICE
==========
-This copyright applies to all files within this subdirectory. All
-other files in the Xen source distribution are covered by version 2 of
-the GNU General Public License.
+This copyright applies to all files within this subdirectory and its
+subdirectories:
+ include/public/*.h
+ include/public/hvm/*.h
+ include/public/io/*.h
+
+The intention is that these files can be freely copied into the source
+tree of an operating system when porting that OS to run on Xen. Doing
+so does *not* cause the OS to become subject to the terms of the GPL.
+
+All other files in the Xen source distribution are covered by version
+2 of the GNU General Public License except where explicitly stated
+otherwise within individual source files.
-- Keir Fraser (on behalf of the Xen team)
diff --git a/xen/include/public/acm.h b/xen/include/public/acm.h
index d53bc6bbd5..23078837fb 100644
--- a/xen/include/public/acm.h
+++ b/xen/include/public/acm.h
@@ -1,6 +1,24 @@
/*
* acm.h: Xen access control module interface defintions
*
+ * 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.
+ *
* Reiner Sailer <sailer@watson.ibm.com>
* Copyright (c) 2005, International Business Machines Corporation.
*/
diff --git a/xen/include/public/acm_ops.h b/xen/include/public/acm_ops.h
index 4a1c3b4d35..5e103dca7b 100644
--- a/xen/include/public/acm_ops.h
+++ b/xen/include/public/acm_ops.h
@@ -1,6 +1,24 @@
/*
* acm_ops.h: Xen access control module hypervisor commands
*
+ * 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.
+ *
* Reiner Sailer <sailer@watson.ibm.com>
* Copyright (c) 2005,2006 International Business Machines Corporation.
*/
diff --git a/xen/include/public/arch-ia64.h b/xen/include/public/arch-ia64.h
index 78f0d57dbf..01fb5f2ec8 100644
--- a/xen/include/public/arch-ia64.h
+++ b/xen/include/public/arch-ia64.h
@@ -2,6 +2,25 @@
* arch-ia64/hypervisor-if.h
*
* Guest OS interface to IA64 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.
+ *
*/
#ifndef __HYPERVISOR_IF_IA64_H__
diff --git a/xen/include/public/arch-x86_32.h b/xen/include/public/arch-x86_32.h
index 90bc4605bf..42651c20b6 100644
--- a/xen/include/public/arch-x86_32.h
+++ b/xen/include/public/arch-x86_32.h
@@ -3,6 +3,24 @@
*
* Guest OS interface to x86 32-bit 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.
+ *
* Copyright (c) 2004, K A Fraser
*/
@@ -172,12 +190,9 @@ struct vcpu_guest_context {
/* FPU registers come first so they can be aligned for FXSAVE/FXRSTOR. */
struct { char x[512]; } fpu_ctxt; /* User-level FPU registers */
#define VGCF_I387_VALID (1<<0)
-#define VGCF_HVM_GUEST (1<<1)
#define VGCF_IN_KERNEL (1<<2)
#define _VGCF_i387_valid 0
#define VGCF_i387_valid (1<<_VGCF_i387_valid)
-#define _VGCF_hvm_guest 1
-#define VGCF_hvm_guest (1<<_VGCF_hvm_guest)
#define _VGCF_in_kernel 2
#define VGCF_in_kernel (1<<_VGCF_in_kernel)
#define _VGCF_failsafe_disables_events 3
diff --git a/xen/include/public/arch-x86_64.h b/xen/include/public/arch-x86_64.h
index 2891cfa29e..a60bc204ef 100644
--- a/xen/include/public/arch-x86_64.h
+++ b/xen/include/public/arch-x86_64.h
@@ -3,6 +3,24 @@
*
* Guest OS interface to x86 64-bit 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.
+ *
* Copyright (c) 2004, K A Fraser
*/
@@ -152,7 +170,7 @@ typedef unsigned long xen_ulong_t;
* directly with
* orb $3,1*8(%rsp)
* iretq
- * If flags contains VGCF_IN_SYSCALL:
+ * If flags contains VGCF_in_syscall:
* Restore RAX, RIP, RFLAGS, RSP.
* Discard R11, RCX, CS, SS.
* Otherwise:
@@ -160,7 +178,9 @@ typedef unsigned long xen_ulong_t;
* All other registers are saved on hypercall entry and restored to user.
*/
/* Guest exited in SYSCALL context? Return to guest with SYSRET? */
-#define VGCF_IN_SYSCALL (1<<8)
+#define _VGCF_in_syscall 8
+#define VGCF_in_syscall (1<<_VGCF_in_syscall)
+#define VGCF_IN_SYSCALL VGCF_in_syscall
struct iret_context {
/* Top of stack (%rsp at point of hypercall). */
uint64_t rax, r11, rcx, flags, rip, cs, rflags, rsp, ss;
@@ -243,12 +263,9 @@ struct vcpu_guest_context {
/* FPU registers come first so they can be aligned for FXSAVE/FXRSTOR. */
struct { char x[512]; } fpu_ctxt; /* User-level FPU registers */
#define VGCF_I387_VALID (1<<0)
-#define VGCF_HVM_GUEST (1<<1)
#define VGCF_IN_KERNEL (1<<2)
#define _VGCF_i387_valid 0
#define VGCF_i387_valid (1<<_VGCF_i387_valid)
-#define _VGCF_hvm_guest 1
-#define VGCF_hvm_guest (1<<_VGCF_hvm_guest)
#define _VGCF_in_kernel 2
#define VGCF_in_kernel (1<<_VGCF_in_kernel)
#define _VGCF_failsafe_disables_events 3
diff --git a/xen/include/public/callback.h b/xen/include/public/callback.h
index b497d99317..cac5389925 100644
--- a/xen/include/public/callback.h
+++ b/xen/include/public/callback.h
@@ -3,6 +3,24 @@
*
* Register guest OS callbacks with 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.
+ *
* Copyright (c) 2006, Ian Campbell
*/
diff --git a/xen/include/public/dom0_ops.h b/xen/include/public/dom0_ops.h
index b03a7ce2ee..5d2b3245c0 100644
--- a/xen/include/public/dom0_ops.h
+++ b/xen/include/public/dom0_ops.h
@@ -3,6 +3,24 @@
*
* Process command requests from domain-0 guest OS.
*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
* Copyright (c) 2002-2003, B Dragovic
* Copyright (c) 2002-2006, K Fraser
*/
diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h
index 6ad8918757..37618ad827 100644
--- a/xen/include/public/domctl.h
+++ b/xen/include/public/domctl.h
@@ -3,6 +3,24 @@
*
* Domain management operations. For use by node control stack.
*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
* Copyright (c) 2002-2003, B Dragovic
* Copyright (c) 2002-2006, K Fraser
*/
@@ -16,7 +34,7 @@
#include "xen.h"
-#define XEN_DOMCTL_INTERFACE_VERSION 0x00000003
+#define XEN_DOMCTL_INTERFACE_VERSION 0x00000004
struct xenctl_cpumap {
XEN_GUEST_HANDLE(uint8_t) bitmap;
@@ -32,6 +50,10 @@ struct xen_domctl_createdomain {
/* IN parameters */
uint32_t ssidref;
xen_domain_handle_t handle;
+ /* Is this an HVM guest (as opposed to a PV guest)? */
+#define _XEN_DOMCTL_CDF_hvm_guest 0
+#define XEN_DOMCTL_CDF_hvm_guest (1U<<_XEN_DOMCTL_CDF_hvm_guest)
+ uint32_t flags;
};
typedef struct xen_domctl_createdomain xen_domctl_createdomain_t;
DEFINE_XEN_GUEST_HANDLE(xen_domctl_createdomain_t);
@@ -44,22 +66,37 @@ DEFINE_XEN_GUEST_HANDLE(xen_domctl_createdomain_t);
struct xen_domctl_getdomaininfo {
/* OUT variables. */
domid_t domain; /* Also echoed in domctl.domain */
-#define DOMFLAGS_DYING (1<<0) /* Domain is scheduled to die. */
-#define DOMFLAGS_SHUTDOWN (1<<2) /* The guest OS has shut down. */
-#define DOMFLAGS_PAUSED (1<<3) /* Currently paused by control software. */
-#define DOMFLAGS_BLOCKED (1<<4) /* Currently blocked pending an event. */
-#define DOMFLAGS_RUNNING (1<<5) /* Domain is currently running. */
-#define DOMFLAGS_CPUMASK 255 /* CPU to which this domain is bound. */
-#define DOMFLAGS_CPUSHIFT 8
-#define DOMFLAGS_SHUTDOWNMASK 255 /* DOMFLAGS_SHUTDOWN guest-supplied code. */
-#define DOMFLAGS_SHUTDOWNSHIFT 16
- uint32_t flags;
+ /* Domain is scheduled to die. */
+#define _XEN_DOMINF_dying 0
+#define XEN_DOMINF_dying (1U<<_XEN_DOMINF_dying)
+ /* Domain is an HVM guest (as opposed to a PV guest). */
+#define _XEN_DOMINF_hvm_guest 1
+#define XEN_DOMINF_hvm_guest (1U<<_XEN_DOMINF_hvm_guest)
+ /* The guest OS has shut down. */
+#define _XEN_DOMINF_shutdown 2
+#define XEN_DOMINF_shutdown (1U<<_XEN_DOMINF_shutdown)
+ /* Currently paused by control software. */
+#define _XEN_DOMINF_paused 3
+#define XEN_DOMINF_paused (1U<<_XEN_DOMINF_paused)
+ /* Currently blocked pending an event. */
+#define _XEN_DOMINF_blocked 4
+#define XEN_DOMINF_blocked (1U<<_XEN_DOMINF_blocked)
+ /* Domain is currently running. */
+#define _XEN_DOMINF_running 5
+#define XEN_DOMINF_running (1U<<_XEN_DOMINF_running)
+ /* CPU to which this domain is bound. */
+#define XEN_DOMINF_cpumask 255
+#define XEN_DOMINF_cpushift 8
+ /* XEN_DOMINF_shutdown guest-supplied code. */
+#define XEN_DOMINF_shutdownmask 255
+#define XEN_DOMINF_shutdownshift 16
+ uint32_t flags; /* XEN_DOMINF_* */
uint64_t tot_pages;
uint64_t max_pages;
uint64_t shared_info_frame; /* MFN of shared_info struct */
uint64_t cpu_time;
- uint32_t nr_online_vcpus; /* Number of VCPUs currently online. */
- uint32_t max_vcpu_id; /* Maximum VCPUID in use by this domain. */
+ uint32_t nr_online_vcpus; /* Number of VCPUs currently online. */
+ uint32_t max_vcpu_id; /* Maximum VCPUID in use by this domain. */
uint32_t ssidref;
xen_domain_handle_t handle;
};
diff --git a/xen/include/public/elfnote.h b/xen/include/public/elfnote.h
index a64d3df5bd..8f03504b1e 100644
--- a/xen/include/public/elfnote.h
+++ b/xen/include/public/elfnote.h
@@ -3,6 +3,24 @@
*
* Definitions used for the Xen ELF notes.
*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
* Copyright (c) 2006, Ian Campbell, XenSource Ltd.
*/
diff --git a/xen/include/public/event_channel.h b/xen/include/public/event_channel.h
index 96c98fd6f8..62cf764040 100644
--- a/xen/include/public/event_channel.h
+++ b/xen/include/public/event_channel.h
@@ -3,6 +3,24 @@
*
* Event channels between domains.
*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
* Copyright (c) 2003-2004, K A Fraser.
*/
diff --git a/xen/include/public/features.h b/xen/include/public/features.h
index c46e3becfe..d4b373ff17 100644
--- a/xen/include/public/features.h
+++ b/xen/include/public/features.h
@@ -3,6 +3,24 @@
*
* Feature flags, reported by XENVER_get_features.
*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
* Copyright (c) 2006, Keir Fraser <keir@xensource.com>
*/
diff --git a/xen/include/public/grant_table.h b/xen/include/public/grant_table.h
index b7b8b6d3e4..9622b56d02 100644
--- a/xen/include/public/grant_table.h
+++ b/xen/include/public/grant_table.h
@@ -4,6 +4,24 @@
* Interface for granting foreign access to page frames, and receiving
* page-ownership transfers.
*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
* Copyright (c) 2004, K A Fraser
*/
diff --git a/xen/include/public/hvm/e820.h b/xen/include/public/hvm/e820.h
index 8190c767df..dc683720bc 100644
--- a/xen/include/public/hvm/e820.h
+++ b/xen/include/public/hvm/e820.h
@@ -1,3 +1,24 @@
+
+/*
+ * 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 __XEN_PUBLIC_HVM_E820_H__
#define __XEN_PUBLIC_HVM_E820_H__
@@ -7,12 +28,6 @@
#define E820_ACPI 3
#define E820_NVS 4
-/* Xen HVM extended E820 types. */
-#define E820_IO 16
-#define E820_SHARED_PAGE 17
-#define E820_XENSTORE 18
-#define E820_BUFFERED_IO 19
-
/* E820 location in HVM virtual address space. */
#define E820_MAP_PAGE 0x00090000
#define E820_MAP_NR_OFFSET 0x000001E8
diff --git a/xen/include/public/hvm/hvm_info_table.h b/xen/include/public/hvm/hvm_info_table.h
index 3b705eeedf..1aa9e73d4f 100644
--- a/xen/include/public/hvm/hvm_info_table.h
+++ b/xen/include/public/hvm/hvm_info_table.h
@@ -2,6 +2,24 @@
* hvm/hvm_info_table.h
*
* HVM parameter and information table, written into guest memory map.
+ *
+ * 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 __XEN_PUBLIC_HVM_HVM_INFO_TABLE_H__
diff --git a/xen/include/public/hvm/hvm_op.h b/xen/include/public/hvm/hvm_op.h
new file mode 100644
index 0000000000..4a03e80ed4
--- /dev/null
+++ b/xen/include/public/hvm/hvm_op.h
@@ -0,0 +1,25 @@
+#ifndef __XEN_PUBLIC_HVM_HVM_OP_H__
+#define __XEN_PUBLIC_HVM_HVM_OP_H__
+
+/* Get/set subcommands: extra argument == pointer to xen_hvm_param struct. */
+#define HVMOP_set_param 0
+#define HVMOP_get_param 1
+struct xen_hvm_param {
+ domid_t domid; /* IN */
+ uint32_t index; /* IN */
+ uint64_t value; /* IN/OUT */
+};
+typedef struct xen_hvm_param xen_hvm_param_t;
+DEFINE_XEN_GUEST_HANDLE(xen_hvm_param_t);
+
+/* Set the logical level of one of a domain's IRQ lines. */
+#define HVMOP_set_irq_level 2
+struct xen_hvm_set_irq_level {
+ domid_t domid; /* Domain to be updated. */
+ uint16_t level; /* New level of the IRQ (0 or 1). */
+ uint32_t irq; /* IRQ to be updated. */
+};
+typedef struct xen_hvm_set_irq_level xen_hvm_set_irq_level_t;
+DEFINE_XEN_GUEST_HANDLE(xen_hvm_set_irq_level_t);
+
+#endif /* __XEN_PUBLIC_HVM_HVM_OP_H__ */
diff --git a/xen/include/public/hvm/ioreq.h b/xen/include/public/hvm/ioreq.h
index 992505e1c5..32aafa2534 100644
--- a/xen/include/public/hvm/ioreq.h
+++ b/xen/include/public/hvm/ioreq.h
@@ -1,20 +1,24 @@
/*
* ioreq.h: I/O request definitions for device models
* Copyright (c) 2004, Intel Corporation.
+ *
+ * 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:
*
- * 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.
+ * 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 _IOREQ_H_
@@ -23,7 +27,7 @@
#define IOREQ_READ 1
#define IOREQ_WRITE 0
-#define STATE_INVALID 0
+#define STATE_IOREQ_NONE 0
#define STATE_IOREQ_READY 1
#define STATE_IOREQ_INPROCESS 2
#define STATE_IORESP_READY 3
@@ -34,6 +38,7 @@
#define IOREQ_TYPE_OR 3
#define IOREQ_TYPE_XOR 4
#define IOREQ_TYPE_XCHG 5
+#define IOREQ_TYPE_ADD 6
/*
* VMExit dispatcher should cooperate with instruction decoder to
@@ -44,12 +49,10 @@ struct ioreq {
uint64_t addr; /* physical address */
uint64_t size; /* size in bytes */
uint64_t count; /* for rep prefixes */
- union {
- uint64_t data; /* data */
- void *pdata; /* pointer to data */
- } u;
+ uint64_t data; /* data (or paddr of data) */
uint8_t state:4;
- uint8_t pdata_valid:1; /* if 1, use pdata above */
+ uint8_t data_is_ptr:1; /* if 1, data above is the guest paddr
+ * of the real data to use. */
uint8_t dir:1; /* 1=read, 0=write */
uint8_t df:1;
uint8_t type; /* I/O type */
@@ -57,14 +60,6 @@ struct ioreq {
};
typedef struct ioreq ioreq_t;
-struct global_iodata {
- uint16_t pic_elcr;
- uint16_t pic_irr;
- uint16_t pic_last_irr;
- uint16_t pic_clear_irr;
-};
-typedef struct global_iodata global_iodata_t;
-
struct vcpu_iodata {
struct ioreq vp_ioreq;
/* Event channel port */
@@ -73,7 +68,6 @@ struct vcpu_iodata {
typedef struct vcpu_iodata vcpu_iodata_t;
struct shared_iopage {
- struct global_iodata sp_global;
struct vcpu_iodata vcpu_iodata[1];
};
typedef struct shared_iopage shared_iopage_t;
diff --git a/xen/include/public/hvm/params.h b/xen/include/public/hvm/params.h
index 432b32cb67..caa1f1f545 100644
--- a/xen/include/public/hvm/params.h
+++ b/xen/include/public/hvm/params.h
@@ -1,24 +1,36 @@
+
+/*
+ * 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 __XEN_PUBLIC_HVM_PARAMS_H__
#define __XEN_PUBLIC_HVM_PARAMS_H__
-/* Parameter space. */
+#include "hvm_op.h"
+
+/* Parameter space for HVMOP_{set,get}_param. */
#define HVM_PARAM_CALLBACK_IRQ 0
#define HVM_PARAM_STORE_PFN 1
#define HVM_PARAM_STORE_EVTCHN 2
-#define HVM_PARAM_APIC_ENABLED 3
#define HVM_PARAM_PAE_ENABLED 4
-#define HVM_NR_PARAMS 5
-
-/* Get/set subcommands: extra argument == pointer to xen_hvm_param struct. */
-#define HVMOP_set_param 0
-#define HVMOP_get_param 1
-
-struct xen_hvm_param {
- domid_t domid; /* IN */
- uint32_t index; /* IN */
- uint64_t value; /* IN/OUT */
-};
-typedef struct xen_hvm_param xen_hvm_param_t;
-DEFINE_XEN_GUEST_HANDLE(xen_hvm_param_t);
+#define HVM_PARAM_IOREQ_PFN 5
+#define HVM_PARAM_BUFIOREQ_PFN 6
+#define HVM_NR_PARAMS 7
#endif /* __XEN_PUBLIC_HVM_PARAMS_H__ */
diff --git a/xen/include/public/hvm/vmx_assist.h b/xen/include/public/hvm/vmx_assist.h
index f987b0fbc2..b07573c211 100644
--- a/xen/include/public/hvm/vmx_assist.h
+++ b/xen/include/public/hvm/vmx_assist.h
@@ -1,6 +1,24 @@
/*
* vmx_assist.h: Context definitions for the VMXASSIST world switch.
*
+ * 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.
+ *
* Leendert van Doorn, leendert@watson.ibm.com
* Copyright (c) 2005, International Business Machines Corporation.
*/
diff --git a/xen/include/public/io/blkif.h b/xen/include/public/io/blkif.h
index e29e400e9b..43fdf6ecbe 100644
--- a/xen/include/public/io/blkif.h
+++ b/xen/include/public/io/blkif.h
@@ -3,6 +3,24 @@
*
* Unified block-device I/O interface for Xen guest OSes.
*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
* Copyright (c) 2003-2004, Keir Fraser
*/
@@ -29,8 +47,22 @@
#endif
#define blkif_sector_t uint64_t
-#define BLKIF_OP_READ 0
-#define BLKIF_OP_WRITE 1
+/*
+ * REQUEST CODES.
+ */
+#define BLKIF_OP_READ 0
+#define BLKIF_OP_WRITE 1
+/*
+ * Recognised only if "feature-barrier" is present in backend xenbus info.
+ * The "feature_barrier" node contains a boolean indicating whether barrier
+ * requests are likely to succeed or fail. Either way, a barrier request
+ * may fail at any time with BLKIF_RSP_EOPNOTSUPP if it is unsupported by
+ * the underlying block-device hardware. The boolean simply indicates whether
+ * or not it is worthwhile for the frontend to attempt barrier requests.
+ * If a backend does not recognise BLKIF_OP_WRITE_BARRIER, it should *not*
+ * create the "feature-barrier" node!
+ */
+#define BLKIF_OP_WRITE_BARRIER 2
/*
* Maximum scatter/gather segments per request.
@@ -61,8 +93,15 @@ struct blkif_response {
};
typedef struct blkif_response blkif_response_t;
-#define BLKIF_RSP_ERROR -1 /* non-specific 'error' */
-#define BLKIF_RSP_OKAY 0 /* non-specific 'okay' */
+/*
+ * STATUS RETURN CODES.
+ */
+ /* Operation not supported (only happens on barrier writes). */
+#define BLKIF_RSP_EOPNOTSUPP -2
+ /* Operation failed for some unspecified reason (-EIO). */
+#define BLKIF_RSP_ERROR -1
+ /* Operation completed successfully. */
+#define BLKIF_RSP_OKAY 0
/*
* Generate blkif ring structures and types.
diff --git a/xen/include/public/io/console.h b/xen/include/public/io/console.h
index cb59b24ffc..4b8c01a447 100644
--- a/xen/include/public/io/console.h
+++ b/xen/include/public/io/console.h
@@ -3,6 +3,24 @@
*
* Console I/O interface for Xen guest OSes.
*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
* Copyright (c) 2005, Keir Fraser
*/
diff --git a/xen/include/public/io/netif.h b/xen/include/public/io/netif.h
index c012d11bfa..a2e885cae5 100644
--- a/xen/include/public/io/netif.h
+++ b/xen/include/public/io/netif.h
@@ -3,6 +3,24 @@
*
* Unified network-device I/O interface for Xen guest OSes.
*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
* Copyright (c) 2003-2004, Keir Fraser
*/
diff --git a/xen/include/public/io/pciif.h b/xen/include/public/io/pciif.h
index a1c9ab7c95..87b64c371f 100644
--- a/xen/include/public/io/pciif.h
+++ b/xen/include/public/io/pciif.h
@@ -1,6 +1,24 @@
/*
* PCI Backend/Frontend Common Data Structures & Macros
*
+ * 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.
+ *
* Author: Ryan Wilson <hap9@epoch.ncsc.mil>
*/
#ifndef __XEN_PCI_COMMON_H__
diff --git a/xen/include/public/io/ring.h b/xen/include/public/io/ring.h
index d427ba6679..704460b599 100644
--- a/xen/include/public/io/ring.h
+++ b/xen/include/public/io/ring.h
@@ -3,6 +3,24 @@
*
* Shared producer-consumer ring macros.
*
+ * 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.
+ *
* Tim Deegan and Andrew Warfield November 2004.
*/
diff --git a/xen/include/public/io/tpmif.h b/xen/include/public/io/tpmif.h
index a088b63098..220671d3ed 100644
--- a/xen/include/public/io/tpmif.h
+++ b/xen/include/public/io/tpmif.h
@@ -3,6 +3,24 @@
*
* TPM I/O interface for Xen guest OSes.
*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
* Copyright (c) 2005, IBM Corporation
*
* Author: Stefan Berger, stefanb@us.ibm.com
diff --git a/xen/include/public/io/xenbus.h b/xen/include/public/io/xenbus.h
index 1c752dfaeb..fed498b3a1 100644
--- a/xen/include/public/io/xenbus.h
+++ b/xen/include/public/io/xenbus.h
@@ -3,6 +3,24 @@
*
* Xenbus protocol details.
*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
* Copyright (C) 2005 XenSource Ltd.
*/
diff --git a/xen/include/public/io/xs_wire.h b/xen/include/public/io/xs_wire.h
index 3c642fd1fc..e948bef29f 100644
--- a/xen/include/public/io/xs_wire.h
+++ b/xen/include/public/io/xs_wire.h
@@ -1,6 +1,25 @@
/*
* Details of the "wire" protocol between Xen Store Daemon and client
* library or guest kernel.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
* Copyright (C) 2005 Rusty Russell IBM Corporation
*/
diff --git a/xen/include/public/memory.h b/xen/include/public/memory.h
index 97cb29a811..e28ed12620 100644
--- a/xen/include/public/memory.h
+++ b/xen/include/public/memory.h
@@ -3,6 +3,24 @@
*
* Memory reservation and information.
*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
* Copyright (c) 2005, Keir Fraser <keir@xensource.com>
*/
diff --git a/xen/include/public/nmi.h b/xen/include/public/nmi.h
index 69c408aa4d..b2b84018a3 100644
--- a/xen/include/public/nmi.h
+++ b/xen/include/public/nmi.h
@@ -3,6 +3,24 @@
*
* NMI callback registration and reason codes.
*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
* Copyright (c) 2005, Keir Fraser <keir@xensource.com>
*/
diff --git a/xen/include/public/physdev.h b/xen/include/public/physdev.h
index 9b0baee573..890f1e3dcd 100644
--- a/xen/include/public/physdev.h
+++ b/xen/include/public/physdev.h
@@ -1,4 +1,24 @@
+/*
+ * 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 __XEN_PUBLIC_PHYSDEV_H__
#define __XEN_PUBLIC_PHYSDEV_H__
diff --git a/xen/include/public/platform.h b/xen/include/public/platform.h
index 46e9160915..2937b4d31d 100644
--- a/xen/include/public/platform.h
+++ b/xen/include/public/platform.h
@@ -3,6 +3,24 @@
*
* Hardware platform operations. Intended for use by domain-0 kernel.
*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
* Copyright (c) 2002-2006, K Fraser
*/
diff --git a/xen/include/public/sched.h b/xen/include/public/sched.h
index abf11cce92..2227a95be6 100644
--- a/xen/include/public/sched.h
+++ b/xen/include/public/sched.h
@@ -3,6 +3,24 @@
*
* Scheduler state interactions
*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
* Copyright (c) 2005, Keir Fraser <keir@xensource.com>
*/
diff --git a/xen/include/public/sysctl.h b/xen/include/public/sysctl.h
index d556270a09..072e06fa4f 100644
--- a/xen/include/public/sysctl.h
+++ b/xen/include/public/sysctl.h
@@ -3,6 +3,24 @@
*
* System management operations. For use by node control stack.
*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
* Copyright (c) 2002-2006, K Fraser
*/
diff --git a/xen/include/public/trace.h b/xen/include/public/trace.h
index 328160cb9e..8f5ef3965c 100644
--- a/xen/include/public/trace.h
+++ b/xen/include/public/trace.h
@@ -1,6 +1,24 @@
/******************************************************************************
* include/public/trace.h
*
+ * 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.
+ *
* Mark Williamson, (C) 2004 Intel Research Cambridge
* Copyright (C) 2005 Bin Ren
*/
diff --git a/xen/include/public/vcpu.h b/xen/include/public/vcpu.h
index 377defe17c..825e849e70 100644
--- a/xen/include/public/vcpu.h
+++ b/xen/include/public/vcpu.h
@@ -3,6 +3,24 @@
*
* VCPU initialisation, query, and hotplug.
*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
* Copyright (c) 2005, Keir Fraser <keir@xensource.com>
*/
diff --git a/xen/include/public/version.h b/xen/include/public/version.h
index dbe18c9e08..944ca620b0 100644
--- a/xen/include/public/version.h
+++ b/xen/include/public/version.h
@@ -3,6 +3,24 @@
*
* Xen version, type, and compile information.
*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
* Copyright (c) 2005, Nguyen Anh Quynh <aquynh@gmail.com>
* Copyright (c) 2005, Keir Fraser <keir@xensource.com>
*/
diff --git a/xen/include/public/xen-compat.h b/xen/include/public/xen-compat.h
index 89f7bd487a..f23abf95d4 100644
--- a/xen/include/public/xen-compat.h
+++ b/xen/include/public/xen-compat.h
@@ -3,6 +3,24 @@
*
* Guest OS interface to Xen. Compatibility layer.
*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
* Copyright (c) 2006, Christian Limpach
*/
diff --git a/xen/include/public/xen.h b/xen/include/public/xen.h
index dd56fc1cae..f44d61fd10 100644
--- a/xen/include/public/xen.h
+++ b/xen/include/public/xen.h
@@ -3,6 +3,24 @@
*
* Guest OS interface to 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.
+ *
* Copyright (c) 2004, K A Fraser
*/
diff --git a/xen/include/public/xenoprof.h b/xen/include/public/xenoprof.h
index a788c36f2b..28152033ef 100644
--- a/xen/include/public/xenoprof.h
+++ b/xen/include/public/xenoprof.h
@@ -4,6 +4,24 @@
* Interface for enabling system wide profiling based on hardware performance
* counters
*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
* Copyright (C) 2005 Hewlett-Packard Co.
* Written by Aravind Menon & Jose Renato Santos
*/
diff --git a/xen/include/xen/config.h b/xen/include/xen/config.h
index f79472da77..068a550078 100644
--- a/xen/include/xen/config.h
+++ b/xen/include/xen/config.h
@@ -12,30 +12,70 @@
#define EXPORT_SYMBOL(var)
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
-/* Linux syslog levels. */
-#define KERN_NOTICE ""
-#define KERN_WARNING ""
-#define KERN_DEBUG ""
-#define KERN_INFO ""
-#define KERN_ERR ""
-#define KERN_CRIT ""
-#define KERN_EMERG ""
-#define KERN_ALERT ""
+/*
+ * The following log levels are as follows:
+ *
+ * XENLOG_ERR: Fatal errors, either Xen, Guest or Dom0
+ * is about to crash.
+ *
+ * XENLOG_WARNING: Something bad happened, but we can recover.
+ *
+ * XENLOG_INFO: Interesting stuff, but not too noisy.
+ *
+ * XENLOG_DEBUG: Use where ever you like. Lots of noise.
+ *
+ *
+ * Since we don't trust the guest operating system, we don't want
+ * it to allow for DoS by causing the HV to print out a lot of
+ * info, so where ever the guest has control of what is printed
+ * we use the XENLOG_GUEST to distinguish that the output is
+ * controlled by the guest.
+ *
+ * To make it easier on the typing, the above log levels all
+ * have a corresponding _G_ equivalent that appends the
+ * XENLOG_GUEST. (see the defines below).
+ *
+ */
+#define XENLOG_ERR "<0>"
+#define XENLOG_WARNING "<1>"
+#define XENLOG_INFO "<2>"
+#define XENLOG_DEBUG "<3>"
+
+#define XENLOG_GUEST "<G>"
+
+#define XENLOG_G_ERR XENLOG_GUEST XENLOG_ERR
+#define XENLOG_G_WARNING XENLOG_GUEST XENLOG_WARNING
+#define XENLOG_G_INFO XENLOG_GUEST XENLOG_INFO
+#define XENLOG_G_DEBUG XENLOG_GUEST XENLOG_DEBUG
+
+/*
+ * Some code is copied directly from Linux.
+ * Match some of the Linux log levels to Xen.
+ */
+#define KERN_ERR XENLOG_ERR
+#define KERN_CRIT XENLOG_ERR
+#define KERN_EMERG XENLOG_ERR
+#define KERN_WARNING XENLOG_WARNING
+#define KERN_NOTICE XENLOG_INFO
+#define KERN_INFO XENLOG_INFO
+#define KERN_DEBUG XENLOG_DEBUG
/* Linux 'checker' project. */
#define __iomem
#define __user
-#ifdef VERBOSE
-#define DPRINTK(_f, _a...) printk("(file=%s, line=%d) " _f, \
- __FILE__ , __LINE__ , ## _a )
-#else
-#define DPRINTK(_f, _a...) ((void)0)
-#endif
-
#ifndef __ASSEMBLY__
+
+int current_domain_id(void);
+#define dprintk(_l, _f, _a...) \
+ printk(_l "%s:%d: " _f, __FILE__ , __LINE__ , ## _a )
+#define gdprintk(_l, _f, _a...) \
+ printk(XENLOG_GUEST _l "%s:%d:d%d " _f, __FILE__, \
+ __LINE__, current_domain_id() , ## _a )
+
#include <xen/compiler.h>
-#endif
+
+#endif /* !__ASSEMBLY__ */
#define __STR(...) #__VA_ARGS__
#define STR(...) __STR(__VA_ARGS__)
diff --git a/xen/include/xen/console.h b/xen/include/xen/console.h
index 12abd2df51..c01dbf23ad 100644
--- a/xen/include/xen/console.h
+++ b/xen/include/xen/console.h
@@ -26,6 +26,9 @@ void console_force_lock(void);
void console_start_sync(void);
void console_end_sync(void);
+void console_start_log_everything(void);
+void console_end_log_everything(void);
+
/*
* Steal output from the console. Returns +ve identifier, else -ve error.
* Takes the handle of the serial line to steal, and steal callback function.
diff --git a/xen/include/xen/domain.h b/xen/include/xen/domain.h
index 40103e7df5..70e8902a73 100644
--- a/xen/include/xen/domain.h
+++ b/xen/include/xen/domain.h
@@ -15,10 +15,20 @@ void free_domain(struct domain *d);
* Arch-specifics.
*/
-struct vcpu *alloc_vcpu_struct(struct domain *d, unsigned int vcpu_id);
-
+/* Allocate/free a VCPU structure. */
+struct vcpu *alloc_vcpu_struct(void);
void free_vcpu_struct(struct vcpu *v);
+/*
+ * Initialise/destroy arch-specific details of a VCPU.
+ * - vcpu_initialise() is called after the basic generic fields of the
+ * VCPU structure are initialised. Many operations can be applied to the
+ * VCPU at this point (e.g., vcpu_pause()).
+ * - vcpu_destroy() is called only if vcpu_initialise() previously succeeded.
+ */
+int vcpu_initialise(struct vcpu *v);
+void vcpu_destroy(struct vcpu *v);
+
int arch_domain_create(struct domain *d);
void arch_domain_destroy(struct domain *d);
diff --git a/xen/include/xen/lib.h b/xen/include/xen/lib.h
index f333c3f14b..e05eac28a9 100644
--- a/xen/include/xen/lib.h
+++ b/xen/include/xen/lib.h
@@ -56,6 +56,8 @@ extern void printk(const char *format, ...)
extern void panic(const char *format, ...)
__attribute__ ((format (printf, 1, 2)));
extern long vm_assist(struct domain *, unsigned int, unsigned int);
+extern int __printk_ratelimit(int ratelimit_ms, int ratelimit_burst);
+extern int printk_ratelimit(void);
/* vsprintf.c */
extern int sprintf(char * buf, const char * fmt, ...)
@@ -80,7 +82,7 @@ long long simple_strtoll(
unsigned long long simple_strtoull(
const char *cp,char **endp, unsigned int base);
-unsigned long long parse_size_and_unit(char *s);
+unsigned long long parse_size_and_unit(const char *s, char **ps);
#define TAINT_UNSAFE_SMP (1<<0)
#define TAINT_MACHINE_CHECK (1<<1)
diff --git a/xen/include/xen/sched-if.h b/xen/include/xen/sched-if.h
index dc3b04fa22..d506d7a589 100644
--- a/xen/include/xen/sched-if.h
+++ b/xen/include/xen/sched-if.h
@@ -63,14 +63,18 @@ struct scheduler {
void (*init) (void);
void (*tick) (unsigned int cpu);
- int (*init_vcpu) (struct vcpu *);
+ int (*init_domain) (struct domain *);
void (*destroy_domain) (struct domain *);
+ int (*init_vcpu) (struct vcpu *);
+ void (*destroy_vcpu) (struct vcpu *);
+
void (*sleep) (struct vcpu *);
void (*wake) (struct vcpu *);
struct task_slice (*do_schedule) (s_time_t);
+ int (*pick_cpu) (struct vcpu *);
int (*adjust) (struct domain *,
struct xen_domctl_scheduler_op *);
void (*dump_settings) (void);
diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
index 93abe4e17f..d08959cab1 100644
--- a/xen/include/xen/sched.h
+++ b/xen/include/xen/sched.h
@@ -144,6 +144,12 @@ struct domain
unsigned long domain_flags;
+ /* Boolean: Is this an HVM guest? */
+ char is_hvm;
+
+ /* Boolean: Is this guest fully privileged (aka dom0)? */
+ char is_privileged;
+
spinlock_t pause_lock;
unsigned int pause_count;
@@ -237,26 +243,30 @@ static inline void get_knownalive_domain(struct domain *d)
ASSERT(!(atomic_read(&d->refcnt) & DOMAIN_DESTROYED));
}
-extern struct domain *domain_create(domid_t domid);
-extern int construct_dom0(
+struct domain *domain_create(domid_t domid, unsigned int domcr_flags);
+ /* DOMCRF_hvm: Create an HVM domain, as opposed to a PV domain. */
+#define _DOMCRF_hvm 0
+#define DOMCRF_hvm (1U<<_DOMCRF_hvm)
+
+int construct_dom0(
struct domain *d,
unsigned long image_start, unsigned long image_len,
unsigned long initrd_start, unsigned long initrd_len,
char *cmdline);
-extern int set_info_guest(struct domain *d, xen_domctl_vcpucontext_t *);
+int set_info_guest(struct domain *d, xen_domctl_vcpucontext_t *);
struct domain *find_domain_by_id(domid_t dom);
-extern void domain_destroy(struct domain *d);
-extern void domain_kill(struct domain *d);
-extern void domain_shutdown(struct domain *d, u8 reason);
-extern void domain_pause_for_debugger(void);
+void domain_destroy(struct domain *d);
+void domain_kill(struct domain *d);
+void domain_shutdown(struct domain *d, u8 reason);
+void domain_pause_for_debugger(void);
/*
* Mark specified domain as crashed. This function always returns, even if the
* caller is the specified domain. The domain is not synchronously descheduled
* from any processor.
*/
-extern void __domain_crash(struct domain *d);
+void __domain_crash(struct domain *d);
#define domain_crash(d) do { \
printk("domain_crash called from %s:%d\n", __FILE__, __LINE__); \
__domain_crash(d); \
@@ -266,7 +276,7 @@ extern void __domain_crash(struct domain *d);
* Mark current domain as crashed and synchronously deschedule from the local
* processor. This function never returns.
*/
-extern void __domain_crash_synchronous(void) __attribute__((noreturn));
+void __domain_crash_synchronous(void) __attribute__((noreturn));
#define domain_crash_synchronous() do { \
printk("domain_crash_sync called from %s:%d\n", __FILE__, __LINE__); \
__domain_crash_synchronous(); \
@@ -281,7 +291,9 @@ void new_thread(struct vcpu *d,
void scheduler_init(void);
void schedulers_start(void);
int sched_init_vcpu(struct vcpu *v, unsigned int processor);
-void sched_destroy_domain(struct domain *);
+void sched_destroy_vcpu(struct vcpu *v);
+int sched_init_domain(struct domain *d);
+void sched_destroy_domain(struct domain *d);
long sched_adjust(struct domain *, struct xen_domctl_scheduler_op *);
int sched_id(void);
void vcpu_wake(struct vcpu *d);
@@ -293,7 +305,7 @@ void vcpu_sleep_sync(struct vcpu *d);
* this call will ensure that all its state is committed to memory and that
* no CPU is using critical state (e.g., page tables) belonging to the VCPU.
*/
-extern void sync_vcpu_execstate(struct vcpu *v);
+void sync_vcpu_execstate(struct vcpu *v);
/*
* Called by the scheduler to switch to another VCPU. This function must
@@ -302,7 +314,7 @@ extern void sync_vcpu_execstate(struct vcpu *v);
* implementing lazy context switching, it suffices to ensure that invoking
* sync_vcpu_execstate() will switch and commit @prev's state.
*/
-extern void context_switch(
+void context_switch(
struct vcpu *prev,
struct vcpu *next);
@@ -312,10 +324,10 @@ extern void context_switch(
* saved to memory. Alternatively, if implementing lazy context switching,
* ensure that invoking sync_vcpu_execstate() will switch and commit @prev.
*/
-extern void context_saved(struct vcpu *prev);
+void context_saved(struct vcpu *prev);
/* Called by the scheduler to continue running the current VCPU. */
-extern void continue_running(
+void continue_running(
struct vcpu *same);
void startup_cpu_idle_loop(void);
@@ -396,26 +408,23 @@ extern struct domain *domain_list;
/*
* Per-domain flags (domain_flags).
*/
- /* Is this domain privileged? */
-#define _DOMF_privileged 0
-#define DOMF_privileged (1UL<<_DOMF_privileged)
/* Guest shut itself down for some reason. */
-#define _DOMF_shutdown 1
+#define _DOMF_shutdown 0
#define DOMF_shutdown (1UL<<_DOMF_shutdown)
/* Death rattle. */
-#define _DOMF_dying 2
+#define _DOMF_dying 1
#define DOMF_dying (1UL<<_DOMF_dying)
/* Domain is paused by controller software. */
-#define _DOMF_ctrl_pause 3
+#define _DOMF_ctrl_pause 2
#define DOMF_ctrl_pause (1UL<<_DOMF_ctrl_pause)
/* Domain is being debugged by controller software. */
-#define _DOMF_debugging 4
+#define _DOMF_debugging 3
#define DOMF_debugging (1UL<<_DOMF_debugging)
/* Are any VCPUs polling event channels (SCHEDOP_poll)? */
-#define _DOMF_polling 5
+#define _DOMF_polling 4
#define DOMF_polling (1UL<<_DOMF_polling)
/* Domain is paused by the hypervisor? */
-#define _DOMF_paused 6
+#define _DOMF_paused 5
#define DOMF_paused (1UL<<_DOMF_paused)
static inline int vcpu_runnable(struct vcpu *v)
@@ -450,11 +459,13 @@ static inline void vcpu_unblock(struct vcpu *v)
vcpu_wake(v);
}
-#define IS_PRIV(_d) \
- (test_bit(_DOMF_privileged, &(_d)->domain_flags))
+#define IS_PRIV(_d) ((_d)->is_privileged)
#define VM_ASSIST(_d,_t) (test_bit((_t), &(_d)->vm_assist))
+#define is_hvm_domain(d) ((d)->is_hvm)
+#define is_hvm_vcpu(v) (is_hvm_domain(v->domain))
+
#endif /* __SCHED_H__ */
/*
diff --git a/xen/include/xen/softirq.h b/xen/include/xen/softirq.h
index 00e561dc86..d4e8edd7ad 100644
--- a/xen/include/xen/softirq.h
+++ b/xen/include/xen/softirq.h
@@ -8,9 +8,8 @@
#define KEYPRESS_SOFTIRQ 3
#define NMI_SOFTIRQ 4
#define PAGE_SCRUB_SOFTIRQ 5
-#define DOMAIN_SHUTDOWN_FINALISE_SOFTIRQ 6
-#define TRACE_SOFTIRQ 7
-#define NR_SOFTIRQS 8
+#define TRACE_SOFTIRQ 6
+#define NR_SOFTIRQS 7
#ifndef __ASSEMBLY__