From 99daef4b23752f76ff11c27f29828f04b2a80f26 Mon Sep 17 00:00:00 2001 From: Andre Richter Date: Tue, 31 Dec 2019 00:04:13 +0100 Subject: [PATCH] Add tutorial 13 --- 13_integrated_testing/.cargo/config | 2 + 13_integrated_testing/.vscode/settings.json | 10 + 13_integrated_testing/Cargo.lock | 187 ++ 13_integrated_testing/Cargo.toml | 52 + 13_integrated_testing/Makefile | 157 ++ 13_integrated_testing/README.md | 1822 +++++++++++++++++ 13_integrated_testing/kernel | Bin 0 -> 152120 bytes 13_integrated_testing/kernel8.img | Bin 0 -> 65568 bytes 13_integrated_testing/patches/Cargo.toml | 5 + 13_integrated_testing/patches/src/lib.rs | 24 + 13_integrated_testing/src/arch.rs | 39 + 13_integrated_testing/src/arch/aarch64.rs | 171 ++ .../src/arch/aarch64/exception.S | 133 ++ .../src/arch/aarch64/exception.rs | 274 +++ 13_integrated_testing/src/arch/aarch64/mmu.rs | 300 +++ .../src/arch/aarch64/sync.rs | 53 + .../src/arch/aarch64/time.rs | 81 + 13_integrated_testing/src/bsp.rs | 41 + 13_integrated_testing/src/bsp/driver.rs | 11 + 13_integrated_testing/src/bsp/driver/bcm.rs | 11 + .../src/bsp/driver/bcm/bcm2xxx_gpio.rs | 148 ++ .../src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs | 340 +++ 13_integrated_testing/src/bsp/rpi.rs | 95 + 13_integrated_testing/src/bsp/rpi/link.ld | 38 + .../src/bsp/rpi/memory_map.rs | 27 + .../src/bsp/rpi/virt_mem_layout.rs | 69 + 13_integrated_testing/src/interface.rs | 142 ++ 13_integrated_testing/src/lib.rs | 69 + 13_integrated_testing/src/main.rs | 93 + 13_integrated_testing/src/memory.rs | 152 ++ 13_integrated_testing/src/panic_wait.rs | 63 + 13_integrated_testing/src/print.rs | 102 + 13_integrated_testing/src/runtime_init.rs | 29 + 13_integrated_testing/test-macros/Cargo.toml | 14 + 13_integrated_testing/test-macros/src/lib.rs | 31 + 13_integrated_testing/test-types/Cargo.toml | 5 + 13_integrated_testing/test-types/src/lib.rs | 16 + .../tests/00_interface_sanity_console.rb | 50 + .../tests/00_interface_sanity_console.rs | 33 + .../tests/01_interface_sanity_timer.rs | 50 + .../tests/02_arch_exception_handling.rs | 42 + .../tests/panic_exit_failure/mod.rs | 9 + .../tests/panic_exit_success/mod.rs | 9 + 13_integrated_testing/tests/runner.rb | 137 ++ doc/testing_demo.gif | Bin 0 -> 167337 bytes 45 files changed, 5136 insertions(+) create mode 100644 13_integrated_testing/.cargo/config create mode 100644 13_integrated_testing/.vscode/settings.json create mode 100644 13_integrated_testing/Cargo.lock create mode 100644 13_integrated_testing/Cargo.toml create mode 100644 13_integrated_testing/Makefile create mode 100644 13_integrated_testing/README.md create mode 100755 13_integrated_testing/kernel create mode 100755 13_integrated_testing/kernel8.img create mode 100644 13_integrated_testing/patches/Cargo.toml create mode 100644 13_integrated_testing/patches/src/lib.rs create mode 100644 13_integrated_testing/src/arch.rs create mode 100644 13_integrated_testing/src/arch/aarch64.rs create mode 100644 13_integrated_testing/src/arch/aarch64/exception.S create mode 100644 13_integrated_testing/src/arch/aarch64/exception.rs create mode 100644 13_integrated_testing/src/arch/aarch64/mmu.rs create mode 100644 13_integrated_testing/src/arch/aarch64/sync.rs create mode 100644 13_integrated_testing/src/arch/aarch64/time.rs create mode 100644 13_integrated_testing/src/bsp.rs create mode 100644 13_integrated_testing/src/bsp/driver.rs create mode 100644 13_integrated_testing/src/bsp/driver/bcm.rs create mode 100644 13_integrated_testing/src/bsp/driver/bcm/bcm2xxx_gpio.rs create mode 100644 13_integrated_testing/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs create mode 100644 13_integrated_testing/src/bsp/rpi.rs create mode 100644 13_integrated_testing/src/bsp/rpi/link.ld create mode 100644 13_integrated_testing/src/bsp/rpi/memory_map.rs create mode 100644 13_integrated_testing/src/bsp/rpi/virt_mem_layout.rs create mode 100644 13_integrated_testing/src/interface.rs create mode 100644 13_integrated_testing/src/lib.rs create mode 100644 13_integrated_testing/src/main.rs create mode 100644 13_integrated_testing/src/memory.rs create mode 100644 13_integrated_testing/src/panic_wait.rs create mode 100644 13_integrated_testing/src/print.rs create mode 100644 13_integrated_testing/src/runtime_init.rs create mode 100644 13_integrated_testing/test-macros/Cargo.toml create mode 100644 13_integrated_testing/test-macros/src/lib.rs create mode 100644 13_integrated_testing/test-types/Cargo.toml create mode 100644 13_integrated_testing/test-types/src/lib.rs create mode 100644 13_integrated_testing/tests/00_interface_sanity_console.rb create mode 100644 13_integrated_testing/tests/00_interface_sanity_console.rs create mode 100644 13_integrated_testing/tests/01_interface_sanity_timer.rs create mode 100644 13_integrated_testing/tests/02_arch_exception_handling.rs create mode 100644 13_integrated_testing/tests/panic_exit_failure/mod.rs create mode 100644 13_integrated_testing/tests/panic_exit_success/mod.rs create mode 100755 13_integrated_testing/tests/runner.rb create mode 100644 doc/testing_demo.gif diff --git a/13_integrated_testing/.cargo/config b/13_integrated_testing/.cargo/config new file mode 100644 index 00000000..e3476485 --- /dev/null +++ b/13_integrated_testing/.cargo/config @@ -0,0 +1,2 @@ +[target.'cfg(target_os = "none")'] +runner = "target/kernel_test_runner.sh" diff --git a/13_integrated_testing/.vscode/settings.json b/13_integrated_testing/.vscode/settings.json new file mode 100644 index 00000000..f2fa6961 --- /dev/null +++ b/13_integrated_testing/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "editor.formatOnSave": true, + "rust.features": [ + "bsp_rpi3" + ], + "rust.all_targets": false, + "editor.rulers": [ + 100 + ], +} \ No newline at end of file diff --git a/13_integrated_testing/Cargo.lock b/13_integrated_testing/Cargo.lock new file mode 100644 index 00000000..77052138 --- /dev/null +++ b/13_integrated_testing/Cargo.lock @@ -0,0 +1,187 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "array-init" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bit_field" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cast" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cortex-a" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "register 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "kernel" +version = "0.1.0" +dependencies = [ + "cortex-a 2.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "patches 0.1.0", + "qemu-exit 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "register 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "test-macros 0.1.0", + "test-types 0.1.0", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "patches" +version = "0.1.0" + +[[package]] +name = "proc-macro2" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "qemu-exit" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "x86_64 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "r0" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "register" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "tock-registers 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "syn" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "test-macros" +version = "0.1.0" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "test-types 0.1.0", +] + +[[package]] +name = "test-types" +version = "0.1.0" + +[[package]] +name = "tock-registers" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "ux" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "x86_64" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "array-init 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ux 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum array-init 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "23589ecb866b460d3a0f1278834750268c607e8e28a1b982c907219f3178cd72" +"checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" +"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +"checksum cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0" +"checksum cortex-a 2.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4aab2f5271d9bf17a52b34dd99993648132df3dacb79312a33332f2b6ae1d0fd" +"checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" +"checksum qemu-exit 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6eef8966dfa42074458b801f4ca70c0a070a84a500022584cc11d7a3c1fdb105" +"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" +"checksum register 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a51f149d257caa7d9aed1f870d573ba5e2429576e6fed0693341f23078c98e55" +"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "dff0acdb207ae2fe6d5976617f887eb1e35a2ba52c13c7234c790960cdad9238" +"checksum tock-registers 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "50af9c49c55cfb4437dd78c1fada3be5d088cbe1bea641db8171283503606a70" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum ux 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "88dfeb711b61ce620c0cb6fd9f8e3e678622f0c971da2a63c4b3e25e88ed012f" +"checksum x86_64 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1f27d9168654aee1b0c1b73746caeb4aa33248f8b8c8f6e100e697fcc2a794b2" diff --git a/13_integrated_testing/Cargo.toml b/13_integrated_testing/Cargo.toml new file mode 100644 index 00000000..347d2f2a --- /dev/null +++ b/13_integrated_testing/Cargo.toml @@ -0,0 +1,52 @@ +[package] +name = "kernel" +version = "0.1.0" +authors = ["Andre Richter "] +edition = "2018" + +[package.metadata.cargo-xbuild] +sysroot_path = "../xbuild_sysroot" + +# The features section is used to select the target board. +[features] +default = [] +bsp_rpi3 = ["cortex-a", "register"] +bsp_rpi4 = ["cortex-a", "register"] + +[dependencies] +r0 = "0.2.*" +qemu-exit = "0.1.x" +test-types = { path = "test-types" } + +# Optional dependencies +cortex-a = { version = "2.8.x", optional = true } +register = { version = "0.4.x", optional = true } + +# Temporary workaround for register-rs. +patches = { path = "patches" } + +##-------------------------------------------------------------------------------------------------- +## Testing +##-------------------------------------------------------------------------------------------------- + +[dev-dependencies] +test-macros = { path = "test-macros" } + +# Unit tests are done in the library part of the kernel. +[lib] +name = "libkernel" +test = true + +# Disable unit tests for the kernel binary. +[[bin]] +name = "kernel" +test = false + +# List of tests without harness. +[[test]] +name = "00_interface_sanity_console" +harness = false + +[[test]] +name = "02_arch_exception_handling" +harness = false diff --git a/13_integrated_testing/Makefile b/13_integrated_testing/Makefile new file mode 100644 index 00000000..9f575d91 --- /dev/null +++ b/13_integrated_testing/Makefile @@ -0,0 +1,157 @@ +## SPDX-License-Identifier: MIT OR Apache-2.0 +## +## Copyright (c) 2018-2019 Andre Richter + +# Default to the RPi3 +ifndef BSP + BSP = rpi3 +endif + +# BSP-specific arguments +ifeq ($(BSP),rpi3) + TARGET = aarch64-unknown-none-softfloat + OUTPUT = kernel8.img + QEMU_BINARY = qemu-system-aarch64 + QEMU_MACHINE_TYPE = raspi3 + QEMU_RELEASE_ARGS = -serial stdio -display none + QEMU_TEST_ARGS = $(QEMU_RELEASE_ARGS) -semihosting + OPENOCD_ARG = -f /openocd/tcl/interface/ftdi/olimex-arm-usb-tiny-h.cfg -f /openocd/rpi3.cfg + JTAG_BOOT_IMAGE = jtag_boot_rpi3.img + LINKER_FILE = src/bsp/rpi/link.ld + RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 +else ifeq ($(BSP),rpi4) + TARGET = aarch64-unknown-none-softfloat + OUTPUT = kernel8.img +# QEMU_BINARY = qemu-system-aarch64 +# QEMU_MACHINE_TYPE = +# QEMU_RELEASE_ARGS = -serial stdio -display none +# QEMU_TEST_ARGS = $(QEMU_RELEASE_ARGS) -semihosting + OPENOCD_ARG = -f /openocd/tcl/interface/ftdi/olimex-arm-usb-tiny-h.cfg -f /openocd/rpi4.cfg + JTAG_BOOT_IMAGE = jtag_boot_rpi4.img + LINKER_FILE = src/bsp/rpi/link.ld + RUSTC_MISC_ARGS = -C target-cpu=cortex-a72 +endif + +# Testing-specific arguments +ifdef TEST + TEST_ARG = --test $(TEST) +endif + +QEMU_MISSING_STRING = "This board is not yet supported for QEMU." + +RUSTFLAGS = -C link-arg=-T$(LINKER_FILE) $(RUSTC_MISC_ARGS) +RUSTFLAGS_PEDANTIC = $(RUSTFLAGS) -D warnings -D missing_docs + +SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) $(wildcard **/*.ld) + +X_CMD_ARGS = --target=$(TARGET) \ + --features bsp_$(BSP) \ + --release + +XRUSTC_CMD = cargo xrustc $(X_CMD_ARGS) +XTEST_CMD = cargo xtest $(X_CMD_ARGS) + +CARGO_OUTPUT = target/$(TARGET)/release/kernel + +OBJCOPY_CMD = cargo objcopy \ + -- \ + --strip-all \ + -O binary + +CONTAINER_UTILS = rustembedded/osdev-utils + +DOCKER_CMD = docker run -it --rm +DOCKER_ARG_CURDIR = -v $(shell pwd):/work -w /work +DOCKER_ARG_TTY = --privileged -v /dev:/dev +DOCKER_ARG_JTAG = -v $(shell pwd)/../X1_JTAG_boot:/jtag +DOCKER_ARG_NET = --network host + +DOCKER_EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) +DOCKER_EXEC_RASPBOOT = raspbootcom +DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyUSB0 +# DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyACM0 + +.PHONY: all doc qemu chainboot jtagboot openocd gdb gdb-opt0 clippy clean readelf objdump nm test + +all: clean $(OUTPUT) + +$(CARGO_OUTPUT): $(SOURCES) + RUSTFLAGS="$(RUSTFLAGS_PEDANTIC)" $(XRUSTC_CMD) + +$(OUTPUT): $(CARGO_OUTPUT) + cp $< . + $(OBJCOPY_CMD) $< $(OUTPUT) + +doc: + cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items + xdg-open target/$(TARGET)/doc/libkernel/index.html + +ifeq ($(QEMU_MACHINE_TYPE),) +qemu: + @echo $(QEMU_MISSING_STRING) + +test: + @echo $(QEMU_MISSING_STRING) +else +qemu: all + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ + $(DOCKER_EXEC_QEMU) $(QEMU_RELEASE_ARGS) \ + -kernel $(OUTPUT) + +define kernel_test_runner + #!/usr/bin/env bash + + $(OBJCOPY_CMD) $$1 $$1.img + TEST_BINARY=$$(echo $$1.img | sed -e 's/.*target/target/g') + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ + ruby tests/runner.rb $(DOCKER_EXEC_QEMU) $(QEMU_TEST_ARGS) -kernel $$TEST_BINARY +endef + +test: $(SOURCES) + @mkdir -p target + $(file > target/kernel_test_runner.sh,$(kernel_test_runner)) + @chmod +x target/kernel_test_runner.sh + RUSTFLAGS="$(RUSTFLAGS_PEDANTIC)" $(XTEST_CMD) $(TEST_ARG) +endif + +chainboot: all + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_TTY) \ + $(CONTAINER_UTILS) $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) \ + $(OUTPUT) + +jtagboot: + $(DOCKER_CMD) $(DOCKER_ARG_TTY) $(DOCKER_ARG_JTAG) $(CONTAINER_UTILS) \ + $(DOCKER_EXEC_RASPBOOT) $(DOCKER_EXEC_RASPBOOT_DEV) \ + /jtag/$(JTAG_BOOT_IMAGE) + +openocd: + $(DOCKER_CMD) $(DOCKER_ARG_TTY) $(DOCKER_ARG_NET) $(CONTAINER_UTILS) \ + openocd $(OPENOCD_ARG) + +define gen_gdb + RUSTFLAGS="$(RUSTFLAGS_PEDANTIC)" $(XRUSTC_CMD) $1 + cp $(CARGO_OUTPUT) kernel_for_jtag + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(DOCKER_ARG_NET) $(CONTAINER_UTILS) \ + gdb-multiarch -q kernel_for_jtag +endef + +gdb: clean $(SOURCES) + $(call gen_gdb,-C debuginfo=2) + +gdb-opt0: clean $(SOURCES) + $(call gen_gdb,-C debuginfo=2 -C opt-level=0) + +clippy: + cargo xclippy --target=$(TARGET) --features bsp_$(BSP) + +clean: + rm -rf target + +readelf: + readelf -a kernel + +objdump: + cargo objdump --target $(TARGET) -- -disassemble -no-show-raw-insn -print-imm-hex kernel + +nm: + cargo nm --target $(TARGET) -- -print-size kernel | sort diff --git a/13_integrated_testing/README.md b/13_integrated_testing/README.md new file mode 100644 index 00000000..9dfc0ed6 --- /dev/null +++ b/13_integrated_testing/README.md @@ -0,0 +1,1822 @@ +# Tutorial 13 - Integrated Testing + +## tl;dr + +- We implement our own test framework using `Rust`'s [custom_test_frameworks] + feature by enabling `Unit Tests` and `Integration Tests` using `QEMU`. +- It is also possible to have test automation for the kernel's `console` + (provided over `UART` in our case): Sending strings/characters to the console + and expecting specific answers in return. + +![Testing live demo](../doc/testing_demo.gif) + +## Table of Contents + +- [Introduction](#introduction) +- [Challenges](#challenges) + * [Acknowledgements](#acknowledgements) +- [Implementation](#implementation) + * [Test Organization](#test-organization) + * [Enabling `custom_test_frameworks` for Unit Tests](#enabling-custom_test_frameworks-for-unit-tests) + + [The Unit Test Runner](#the-unit-test-runner) + + [Calling the Test `main()` Function](#calling-the-test-main-function) + * [Quitting QEMU with user-defined Exit Codes](#quitting-qemu-with-user-defined-exit-codes) + + [Exiting Unit Tests](#exiting-unit-tests) + * [Controlling Test Kernel Execution](#controlling-test-kernel-execution) + + [Wrapping QEMU Test Execution](#wrapping-qemu-test-execution) + * [Writing Unit Tests](#writing-unit-tests) + * [Integration Tests](#integration-tests) + + [Test Harness](#test-harness) + + [No Test Harness](#no-test-harness) + + [Overriding Panic Behavior](#overriding-panic-behavior) + + [Console Tests](#console-tests) +- [Test it](#test-it) +- [Diff to previous](#diff-to-previous) + +## Introduction + +Through the course of the previous tutorials, we silently started to adopt a kind of anti-pattern: +Using the kernel's main function to not only boot the target, but also test or showcase +functionality. For example: + - Stalling execution during boot to test the kernel's timekeeping code by spinning for 1 second. + - Willingly causing exceptions to see the exception handler running. + +The feature set of the kernel is now rich enough so that it makes sense to introduce proper testing +modeled after Rust's [native testing framework]. This tutorial extends our kernel with three basic +testing facilities: + - Classic `Unit Tests`. + - [Integration Tests] (self-contained tests stored in the `$CRATE/tests/` directory). + - `Integration Tests` acting on external stimuli - aka `console` input. Sending strings/characters + to the console and expecting specific answers in return. + +[native testing framework]: https://doc.rust-lang.org/book/ch11-00-testing.html + +## Challenges + +Testing Rust `#![no_std]` code like our kernel is, at the point of writing this tutorial, not an +easy endeavor. The short version is: We cannot use Rust's [native testing +framework](https://doc.rust-lang.org/book/ch11-00-testing.html) straight away. Utilizing the +`#[test]` attribute macro and running `cargo test` (`xtest` in our case) would throw compilation +errors, because there are dependencies on the standard library. + +We have to fall back to Rust's unstable [custom_test_frameworks] feature. It relieves us from +dependencies on the standard library, but comes at the cost of having a reduced feature set. Instead +of annotating functions with `#[test]`, the `#[test_case]` attribute must be used. Additionally, we +need to write a `test_runner` function, which is supposed to execute all the functions annotated +with `#[test_case]`. This is barely enough to get `Unit Tests` running, though. There will be some +more challenges that need solving for getting `Integration Tests` running as well. + +Please note that for automation purposes, all testing will be done in `QEMU` and not on real +hardware. + +[custom_test_frameworks]: https://doc.rust-lang.org/unstable-book/language-features/custom-test-frameworks.html +[Integration Tests]: https://doc.rust-lang.org/book/ch11-03-test-organization.html#integration-tests + +### Acknowledgements + +On this occasion, kudos to [@phil-opp] for his x86-based [testing] article. It helped a lot in +putting together this tutorial. Please go ahead and read it for a different perspective and +additional insights. + +[testing]: https://os.phil-opp.com/testing + +## Implementation + +We introduce a new `Makefile` target: + +```shell +make test +``` + +In essence, `make test` will execute `cargo xtest` instead of `cargo xrustc`. The details will be +explained in due course. The rest of the tutorial will explain as chronologically as possible what +happens when `make test` aka `cargo xtest` runs. + +### Test Organization + +Until now, our kernel was a so-called `binary crate`. As [explained in the official Rust book], this +crate type disallows having `integration tests`. Quoting the book: + +> If our project is a binary crate that only contains a _src/main.rs_ file and doesn’t have a +> _src/lib.rs_ file, we can’t create integration tests in the _tests_ directory and bring functions +> defined in the _src/main.rs_ file into scope with a `use` statement. Only library crates expose +> functions that other crates can use; binary crates are meant to be run on their own. + +> This is one of the reasons Rust projects that provide a binary have a straightforward +> _src/main.rs_ file that calls logic that lives in the _src/lib.rs_ file. Using that structure, +> integration tests _can_ test the library crate with `use` to make the important functionality +> available. If the important functionality works, the small amount of code in the _src/main.rs_ +> file will work as well, and that small amount of code doesn’t need to be tested. + +So let's do that first: We add a `lib.rs` to our crate that aggregates and exports the lion's share +of the kernel code. The `main.rs` file is stripped down to the minimum. It only keeps the +`kernel_init() -> !` and `kernel_main() -> !` functions, everything else is brought into scope with +`use` statements. + +Since it is not possible to use `kernel` as the name for both the library and the binary part of the +crate, new entries in `Cargo.toml` are needed to differentiate the names. What's more, `cargo xtest` +would try to compile and run `unit tests` for both. In our case, it will be sufficient to have all +the unit test code in `lib.rs`, so test generation for `main.rs` can be disabled in `Cargo.toml` as +well through the `test` flag: + +```toml +[lib] +name = "libkernel" +test = true + +[[bin]] +name = "kernel" +test = false +``` + +[explained in the official Rust book]: https://doc.rust-lang.org/book/ch11-03-test-organization.html#integration-tests-for-binary-crates + +### Enabling `custom_test_frameworks` for Unit Tests + +In `lib.rs`, we add the following headers to get started with `custom_test_frameworks`: + +```rust +// Testing +#![cfg_attr(test, no_main)] +#![feature(custom_test_frameworks)] +#![reexport_test_harness_main = "test_main"] +#![test_runner(crate::test_runner)] +``` + +Since this is a library now, we do not keep the `#![no_main]` inner attribute that `main.rs` has, +because a library has no `main()` entry function, so the attribute does not apply. When compiling +for testing, though, it is still needed. The reason is that `cargo xtest` basically turns `lib.rs` +into a binary again by inserting a generated `main()` function (which is then calling a function +that runs all the unit tests, but more about that in a second...). + +However, since our kernel code [overrides the compiler-inserted `main` shim] by way of using +`#![no_main]`, we need the same when `cargo xtest` is producing its test kernel binary. After all, +what we want is a minimal kernel that boots on the target and runs its own unit tests. Therefore, we +conditionally set this attribute (`#![cfg_attr(test, no_main)]`) when the `test` flag is set, which +it is when `cargo xtest` runs. + +[overrides the compiler-inserted `main` shim]: https://doc.rust-lang.org/unstable-book/language-features/lang-items.html?highlight=no_main#writing-an-executable-without-stdlib + +#### The Unit Test Runner + +The `#![test_runner(crate::test_runner)]` attribute declares the path of the test runner function +that we are supposed to provide. This is the one that will be called by the `cargo xtest` generated +`main()` function. Here is the implementation in `lib.rs`: + +```rust +/// The default runner for unit tests. +pub fn test_runner(tests: &[&test_types::UnitTest]) { + println!("Running {} tests", tests.len()); + println!("-------------------------------------------------------------------\n"); + for (i, test) in tests.iter().enumerate() { + print!("{:>3}. {:.<58}", i + 1, test.name); + + // Run the actual test. + (test.test_func)(); + + // Failed tests call panic!(). Execution reaches here only if the test has passed. + println!("[ok]") + } +} +``` + +The function signature shows that `test_runner` takes one argument: A slice of +`test_types::UnitTest` references. This type definition lives in an external crate stored at +`$ROOT/test_types`. It is external because the type is also needed for a self-made [procedural +macro](https://doc.rust-lang.org/reference/procedural-macros.html) that we'll use to write unit +tests, and procedural macros _have_ to live in their own crate. So to avoid a circular dependency +between kernel and proc-macro, this split was needed. Anyways, here is the type definition: + +```rust +/// Unit test container. +pub struct UnitTest { + /// Name of the test. + pub name: &'static str, + + /// Function pointer to the test. + pub test_func: fn(), +} +``` + +A `UnitTest` provides a name and a classic function pointer to the unit test function. The +`test_runner` just iterates over the slice, prints the respective test's name and calls the test +function. + +The convetion is that as long as the test function does not `panic!`, the test was successful. + +#### Calling the Test `main()` Function + +The last of the attributes we added is `#![reexport_test_harness_main = "test_main"]`. Remember that +our kernel uses the `no_main` attribute, and that we also set it for the test compilation. We did +that because we wrote our own `_start()` function (in `aarch64.rs`), which kicks off the following +call chain during kernel boot: + +| | Function | File | +| - | - | - | +| 1. | `_start()` | `lib.rs` | +| 2. | (some more arch code) | `lib.rs` | +| 3. | `runtime_init()` | `lib.rs` | +| 4. | `kernel_init()` | `main.rs` | +| 5. | `kernel_main()` | `main.rs` | + +A function named `main` is never called. Hence, the `main()` function generated by `cargo xtest` + would be silently dropped, and therefore the tests would never be executed. As you can see, + `runtime_init()` is the last function residing in our carved-out `lib.rs`, and it calls into + `kernel_init()`. So in order to get the tests to execute, we add a test-environment version of + `kernel_init()` to `lib.rs` as well (conditional compilation ensures it is only present when the + test flag is set), and call the `cargo xtest` generated `main()` function from there. + +This is where `#![reexport_test_harness_main = "test_main"]` finally comes into picture. It declares +the name of the generated main function so that we can manually call it. Here is the final +implementation in `lib.rs`: + +```rust +/// The `kernel_init()` for unit tests. Called from `runtime_init()`. +#[cfg(test)] +#[no_mangle] +unsafe fn kernel_init() -> ! { + bsp::qemu_bring_up_console(); + + test_main(); + + arch::qemu_exit_success() +} +``` + +Note that we first call `bsp::qemu_bring_up_console()`. Since we are running all our tests inside +`QEMU`, we need to ensure that whatever peripheral implements the kernel's `console` is initialized, +so that we can print from our tests. If you recall [tutorial 03](../03_hacky_hello_world), bringing +up peripherals in `QEMU` might not need the full initialization as is needed on real hardware +(setting clocks, config registers, etc...) due to the abstractions in `QEMU`'s emulation code, so +this is an opportunity to cut down on setup code. + +As a matter of fact, for the `RPis`, nothing needs to be done and the function is empy. But this +might be different for other hardware emulated by QEMU, so it makes sense to introduce the function +now to make it easier in case new `BSPs` are added to the kernel in the future. + +Next, the reexported `test_main()` is called, which will call our `test_runner()` which finally +prints the unit test names and executes them. + +### Quitting QEMU with user-defined Exit Codes + +Let's recap where we are right now: + +We've enabled `custom_test_frameworks` in `lib.rs` to a point where, when using `make test`, the +code gets compiled to a test kernel binary that eventually executes all the (yet-to-be-defined) +`UnitTest` instances by executing all the way from `_start()` to our `test_runner()` function. + +Through mechanisms that are explained later, `cargo` will now instantiate a `QEMU` process that +exectues this test kernel. The question now is: How is test success/failure communicated to `cargo`? +Answer: `cargo` inspects `QEMU`'s [exit status]: + + - `0` translates to testing was successful. + - `non-0` means failure. + +Hence, we need a clever trick now so that our Rust kernel code can get `QEMU` to exit itself with an +exit status that the kernel code supplies. In [@phil-opp]'s testing article, you [learned how to do +this] for `x86 QEMU` systems by using a special `ISA` debug-exit device. Unfortunately, we can't +have that one for our `aarch64` system because it is not compatible. + +In our case, we can leverage the ARM [semihosting] emulation of `QEMU` and do a `SYS_EXIT` +semihosting call with an additional parameter for the exit code. I've written a separate crate, +[qemu-exit], to do this, so let us import it. Specifically, the following two functions: + +```rust +qemu_exit::aarch64::exit_success() // QEMU binary executes `exit(0)`. +qemu_exit::aarch64::exit_failure() // QEMU binary executes `exit(1)`. +``` + +[Click here](https://github.com/andre-richter/qemu-exit/blob/master/src/aarch64.rs) in case you are +interested in the implementation. Note that for the functions to work, the `-semihosting` flag must +be added to the `QEMU` invocation. + +[exit status]: https://en.wikipedia.org/wiki/Exit_status +[@phil-opp]: https://github.com/phil-opp +[learned how to do this]: https://os.phil-opp.com/testing/#exiting-qemu +[semihosting]: https://static.docs.arm.com/100863/0200/semihosting.pdf +[qemu-exit]: https://github.com/andre-richter/qemu-exit + +#### Exiting Unit Tests + +Unit test failure shall be triggered by the `panic!` macro, either directly or by way of using +`assert!` macros. Until now, our `panic!` implementation finally called `arch::wait_forever()` to +safely park the panicked CPU core in a busy loop. This can't be used for the unit tests, because +`cargo` would wait forever for `QEMU` to exit and stall the whole test run. Again, conditional +compilation is used to differentiate between a release and testing version of how a `panic!` +concludes. Here is the new testing version: + +```rust +/// The point of exit when the library is compiled for testing. +#[cfg(test)] +#[no_mangle] +fn _panic_exit() -> ! { + arch::qemu_exit_failure() +} +``` + +In case none of the unit tests panicked, `lib.rs`'s `kernel_init()` calls +`arch::qemu_exit_success()` to successfully conclude the unit test run. + +### Controlling Test Kernel Execution + +Now is a good time to catch up on how the test kernel binary is actually being executed. Normally, +`cargo test` would try to execute the compiled binary as a normal child process. This would fail +horribly because we build a kernel, and not a userspace process. Also, chances are very high that +you sit in front of an `x86` machine, whereas the RPi kernel is `AArch64`. + +Therefore, we need to install some hooks that make sure the test kernel gets executed inside `QEMU`, +quite like it is done for the existing `make qemu` target that is in place since tutorial 1. The +first step is to add a new file to the project, `.cargo/config`: + +```toml +[target.'cfg(target_os = "none")'] +runner = "target/kernel_test_runner.sh" +``` + +Instead of executing a compilation result directly, the `runner` flag will instruct `cargo` to +delegate the execution. Using the setting depicted above, `target/kernel_test_runner.sh` will be +executed and given the full path to the compiled test kernel as the first command line argument. + +The file `kernel_test_runner.sh` does not exist by default. We generate it on demand throguh the +`make test` target: + +```Makefile +define kernel_test_runner + #!/usr/bin/env bash + + $(OBJCOPY_CMD) $$1 $$1.img + TEST_BINARY=$$(echo $$1.img | sed -e 's/.*target/target/g') + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ + ruby tests/runner.rb $(DOCKER_EXEC_QEMU) $(QEMU_TEST_ARGS) -kernel $$TEST_BINARY +endef + +test: $(SOURCES) + @mkdir -p target + $(file > target/kernel_test_runner.sh,$(kernel_test_runner)) + @chmod +x target/kernel_test_runner.sh + RUSTFLAGS="$(RUSTFLAGS_PEDANTIC)" $(XTEST_CMD) $(TEST_ARG) +endif +``` + +It first does the standard `objcopy` step to strip the `ELF` down to a raw binary. Just like in all +the other Makefile targets. Next, the script generates a relative path from the absolute path +provided to it by `cargo`, and finally compiles a `docker` command to execute the test kernel. For +reference, here it is fully resolved for an `RPi3 BSP`: + +```bash +docker run -it --rm -v /opt/rust-raspi3-OS-tutorials/13_integrated_testing:/work -w /work rustembedded/osdev-utils ruby tests/runner.rb qemu-system-aarch64 -M raspi3 -serial stdio -display none -semihosting -kernel $TEST_BINARY +``` + +We're still not done with all the redirections. Spotted the `ruby tests/runner.rb` part that gets +excuted inside Docker? + +#### Wrapping QEMU Test Execution + +`runner.rb` is a [Ruby] wrapper script around `QEMU` that, for unit tests, catches the case that a +test gets stuck, e.g. in an unintentional busy loop or a crash. If `runner.rb` does not observe any +output of the test kernel for `5 seconds`, it cancels the execution and reports a failure back to +`cargo`. If `QEMU` exited itself by means of `aarch64::exit_success() / aarch64::exit_failure()`, +the respective exit status code is passed through. The essential part happens here in `class +RawTest`: + +```ruby +def exec + error = 'Timed out waiting for test' + io = IO.popen(@qemu_cmd) + + while IO.select([io], nil, nil, MAX_WAIT_SECS) + begin + @output << io.read_nonblock(1024) + rescue EOFError + error = false + break + end + end +``` + +[Ruby]: https://www.ruby-lang.org/ + +### Writing Unit Tests + +Alright, that's a wrap for the whole chain from `make test` all the way to reporting the test exit +status back to `cargo xtest`. It is a lot to digest already, but we haven't even learned to write +`Unit Tests` yet. + +In essence, it is almost like in `std` environments, with the difference that `#[test]` can't be +used, because it is part of the standard library. The `no_std` replacement attribute provided by +`custom_test_frameworks` is `#[test_case]`. You can put `#[test_case]` before functions, constants +or statics (you have to decide for one and stick with it). Each attributed item is added to the +"list" that is then passed to the `test_runner` function. + +As you learned earlier, we decided that our tests shall be instances of `test_types::UnitTest`. Here +is the type definition again: + +```rust +/// Unit test container. +pub struct UnitTest { + /// Name of the test. + pub name: &'static str, + + /// Function pointer to the test. + pub test_func: fn(), +} +``` + +So what we could do now is write something like: + +```rust +#[cfg(test)] +mod tests { + use super::*; + + #[test_case] + const TEST1: test_types::UnitTest = test_types::UnitTest { + name: "test_runner_executes_in_kernel_mode", + test_func: || { + let (level, _) = state::current_privilege_level(); + + assert!(level == PrivilegeLevel::Kernel) + }, + }; +} +``` + +Since this is a bit boiler-platy with the const and name definition, let's write a [procedural +macro] named `#[kernel_test]` to simplify this. It should work this way: + + 1. Must be put before functions that take no arguments and return nothing. + 2. Automatically constructs a `const UnitTest` from attributed functions like shown above by: + 1. Converting the function name to the `name` member of the `UnitTest` struct. + 2. Populating the `test_func` member with a closure that executes the body of the attributed + function. + +For the sake of brevity, we're not going to discuss the macro implementation. [Click +here](test-macros/src/lib.rs) if you're interested in it. Using the macro, the example shown before +now boils down to this (this is now an actual example from [arch.rs](src/arch.rs)): + +```rust +#[cfg(test)] +mod tests { + use super::*; + use test_macros::kernel_test; + + /// Libkernel unit tests must execute in kernel mode. + #[kernel_test] + fn test_runner_executes_in_kernel_mode() { + let (level, _) = state::current_privilege_level(); + + assert!(level == PrivilegeLevel::Kernel) + } +} +``` + +Note that since proc macros need to live in their own crates, we need to create a new one at +`$ROOT/test-macros` and save it there. + +Aaaaaand that's how you write unit tests. We're finished with that part for good now :raised_hands:. + +[procedural macro]: https://doc.rust-lang.org/reference/procedural-macros.html + +### Integration Tests + +We are still not done with the tutorial, though :scream:. + +Integration tests need some special attention here and there too. As you already learned, they live +in `$CRATE/tests/`. Each `.rs` file in there gets compiled into its own test kernel binary and +executed separately by `cargo xtest`. The code in the integration tests includes the library part of +our kernel (`libkernel`) through `use` statements. + +Also note that the entry point for each `integration test` must be the `kernel_init()` function +again, just like in the `unit test` case. + +#### Test Harness + +By default, `cargo xtest` will pull in the test harness (that's the official name for the generated +`main()` function) into integration tests as well. This gives you a further means of partitioning +your test code into individual chunks. For example, take a look at +`tests/01_interface_sanity_timer.rs`: + +```rust +//! Timer sanity tests. + +#![feature(custom_test_frameworks)] +#![no_main] +#![no_std] +#![reexport_test_harness_main = "test_main"] +#![test_runner(libkernel::test_runner)] + +mod panic_exit_failure; + +use core::time::Duration; +use libkernel::{arch, arch::timer, bsp, interface::time::Timer}; +use test_macros::kernel_test; + +#[no_mangle] +unsafe fn kernel_init() -> ! { + bsp::qemu_bring_up_console(); + + // Depending on CPU arch, some timer bring-up code could go here. Not needed for the RPi. + + test_main(); + + arch::qemu_exit_success() +} + +/// Simple check that the timer is running. +#[kernel_test] +fn timer_is_counting() { + assert!(timer().uptime().as_nanos() > 0) +} + +/// Timer resolution must be sufficient. +#[kernel_test] +fn timer_resolution_is_sufficient() { + assert!(timer().resolution().as_nanos() < 100) +} +``` + +Note how the `test_runner` from `libkernel` is pulled in through +`#![test_runner(libkernel::test_runner)]`. + +#### No Test Harness + +For some tests, however, it is not needed to have the harness, because there is no need or +possibility to partition the test into individual pieces. In this case, all the test code can live +in `kernel_init()`, and harness generation can be turned off through `Cargo.toml`. This tutorial +introduces two tests that don't need a harness. Here is how harness generation is turned off for +them: + +```toml +# List of tests without harness. +[[test]] +name = "00_interface_sanity_console" +harness = false + +[[test]] +name = "02_arch_exception_handling" +harness = false +``` + +#### Overriding Panic Behavior + +It is also important to understand that the `libkernel` made available to the integration tests is +the _release_ version. Therefore, it won't contain any code attributed with `#[cfg(test)]`! + +One of the implications of this is that the `panic handler` provided by `libkernel` will be the +version from the release kernel that spins forever, and not the test version that exits `QEMU`. + +One way to navigate around this is to declare the _release version of the panic exit function_ in +`lib.rs` as a [weak symbol]: + +```rust +#[cfg(not(test))] +#[linkage = "weak"] +#[no_mangle] +fn _panic_exit() -> ! { + arch::wait_forever() +} +``` + +[weak symbol]: https://en.wikipedia.org/wiki/Weak_symbol + +Integration tests in `$CRATE/tests/` can now override it according to their needs, because depending +on the kind of test, a `panic!` could mean success or failure. For example, +`tests/02_arch_exception_handling.rs` is intentionally causing a page fault, so the wanted outcome +is a `panic!`. Here is the whole test (minus some inline comments): + +```rust +//! Page faults must result in synchronous exceptions. + +#![feature(format_args_nl)] +#![no_main] +#![no_std] + +mod panic_exit_success; + +use libkernel::{arch, bsp, interface::mm::MMU, println}; + +#[no_mangle] +unsafe fn kernel_init() -> ! { + bsp::qemu_bring_up_console(); + + println!("Testing synchronous exception handling by causing a page fault"); + println!("-------------------------------------------------------------------\n"); + + arch::enable_exception_handling(); + + if let Err(string) = arch::mmu().init() { + println!("MMU: {}", string); + arch::qemu_exit_failure() + } + + println!("Writing beyond mapped area to address 9 GiB..."); + let big_addr: u64 = 9 * 1024 * 1024 * 1024; + core::ptr::read_volatile(big_addr as *mut u64); + + // If execution reaches here, the memory access above did not cause a page fault exception. + arch::qemu_exit_failure() +} +``` + +The `_panic_exit()` version that makes `QEMU` return `0` (indicating test success) is pulled in by +`mod panic_exit_success;`. The counterpart would be `mod panic_exit_failure;`. We provide both in +the `tests` folder, so each integration test can import the one that it needs. + +#### Console Tests + +As the kernel or OS grows, it will be more and more interesting to test user/kernel interaction +through the serial console. That is, sending strings/characters to the console and expecting +specific answers in return. The `runner.rb` wrapper script provides infrastructure to do this with +little overhead. It basically works like this: + + 1. For each integration test, check if a companion file to the `.rs` test file exists. + - A companion file has the same name, but ends in `.rb`. + - The companion file contains one or more console subtests. + 2. If it exists, load the file to dynamically import the console subtests. + 3. Spawn `QEMU` and attach to the serial console. + 4. Run the console subtests. + +Here is an excerpt from `00_interface_sanity_console.rb` showing a subtest that does a handshake +with the kernel over the console: + +```ruby +TIMEOUT_SECS = 3 + +# Verify sending and receiving works as expected. +class TxRxHandshake + def name + 'Transmit and Receive handshake' + end + + def run(qemu_out, qemu_in) + qemu_in.write_nonblock('ABC') + raise('TX/RX test failed') if qemu_out.expect('OK1234', TIMEOUT_SECS).nil? + end +end +``` + +The subtest first sends `"ABC"` over the console to the kernel, and then expects to receive +`"OK1234"` back. On the kernel side, it looks like this in `00_interface_sanity_console.rs`: + +```rust +#![feature(format_args_nl)] +#![no_main] +#![no_std] + +mod panic_exit_failure; + +use libkernel::{bsp, interface::console::*, print}; + +#[no_mangle] +unsafe fn kernel_init() -> ! { + bsp::qemu_bring_up_console(); + + // Handshake + assert_eq!(bsp::console().read_char(), 'A'); + assert_eq!(bsp::console().read_char(), 'B'); + assert_eq!(bsp::console().read_char(), 'C'); + print!("OK1234"); +``` + +## Test it + +Believe it or not, that is all. There is two ways you can run tests: + + 1. `make test` will run all tests back-to-back. + 2. `TEST=TEST_NAME make test` will run a specficic integration test. + - For example, `TEST=01_interface_sanity_timer make test` + +```console +make test + +RUSTFLAGS="-C link-arg=-Tsrc/bsp/rpi/link.ld -C target-cpu=cortex-a53 -D warnings -D missing_docs" + cargo xtest --target=aarch64-unknown-none-softfloat --features bsp_rpi3 --release + Compiling kernel v0.1.0 (/opt/rust-raspi3-OS-tutorials/13_integrated_testing) + Finished release [optimized] target(s) in 0.74s + Running target/aarch64-unknown-none-softfloat/release/deps/libkernel-a8441de115ec3a67 + ------------------------------------------------------------------- + 🦀 Running 2 tests + ------------------------------------------------------------------- + + 1. test_runner_executes_in_kernel_mode.......................[ok] + 2. virt_mem_layout_has_no_overlaps...........................[ok] + + ------------------------------------------------------------------- + ✔️ Success: libkernel + ------------------------------------------------------------------- + + + Running target/aarch64-unknown-none-softfloat/release/deps/00_interface_sanity_console-e0e4e8cc44addccc + ------------------------------------------------------------------- + 🦀 Running 3 console-based tests + ------------------------------------------------------------------- + + 1. Transmit and Receive handshake............................[ok] + 2. Transmit statistics.......................................[ok] + 3. Receive statistics........................................[ok] + + ------------------------------------------------------------------- + ✔️ Success: 00_interface_sanity_console + ------------------------------------------------------------------- + + + Running target/aarch64-unknown-none-softfloat/release/deps/01_interface_sanity_timer-a8599d689482115e + ------------------------------------------------------------------- + 🦀 Running 3 tests + ------------------------------------------------------------------- + + 1. timer_is_counting.........................................[ok] + 2. timer_resolution_is_sufficient............................[ok] + 3. spin_accuracy_check_1_second..............................[ok] + + ------------------------------------------------------------------- + ✔️ Success: 01_interface_sanity_timer + ------------------------------------------------------------------- + + + Running target/aarch64-unknown-none-softfloat/release/deps/02_arch_exception_handling-f2ad3f66018143ba + ------------------------------------------------------------------- + 🦀 Testing synchronous exception handling by causing a page fault + ------------------------------------------------------------------- + + Writing beyond mapped area to address 9 GiB... + Kernel panic: + + CPU Exception! + FAR_EL1: 0x0000000240000000 + ESR_EL1: 0x96000004 + [...] + + ------------------------------------------------------------------- + ✔️ Success: 02_arch_exception_handling + ------------------------------------------------------------------- +``` + +## Diff to previous +```diff + +diff -uNr 12_cpu_exceptions_part1/.cargo/config 13_integrated_testing/.cargo/config +--- 12_cpu_exceptions_part1/.cargo/config ++++ 13_integrated_testing/.cargo/config +@@ -0,0 +1,2 @@ ++[target.'cfg(target_os = "none")'] ++runner = "target/kernel_test_runner.sh" + +diff -uNr 12_cpu_exceptions_part1/Cargo.toml 13_integrated_testing/Cargo.toml +--- 12_cpu_exceptions_part1/Cargo.toml ++++ 13_integrated_testing/Cargo.toml +@@ -15,7 +15,38 @@ + + [dependencies] + r0 = "0.2.*" ++qemu-exit = "0.1.x" ++test-types = { path = "test-types" } + + # Optional dependencies + cortex-a = { version = "2.8.x", optional = true } + register = { version = "0.4.x", optional = true } ++ ++# Temporary workaround for register-rs. ++patches = { path = "patches" } ++ ++##-------------------------------------------------------------------------------------------------- ++## Testing ++##-------------------------------------------------------------------------------------------------- ++ ++[dev-dependencies] ++test-macros = { path = "test-macros" } ++ ++# Unit tests are done in the library part of the kernel. ++[lib] ++name = "libkernel" ++test = true ++ ++# Disable unit tests for the kernel binary. ++[[bin]] ++name = "kernel" ++test = false ++ ++# List of tests without harness. ++[[test]] ++name = "00_interface_sanity_console" ++harness = false ++ ++[[test]] ++name = "02_arch_exception_handling" ++harness = false + +diff -uNr 12_cpu_exceptions_part1/Makefile 13_integrated_testing/Makefile +--- 12_cpu_exceptions_part1/Makefile ++++ 13_integrated_testing/Makefile +@@ -13,7 +13,8 @@ + OUTPUT = kernel8.img + QEMU_BINARY = qemu-system-aarch64 + QEMU_MACHINE_TYPE = raspi3 +- QEMU_MISC_ARGS = -serial stdio -display none ++ QEMU_RELEASE_ARGS = -serial stdio -display none ++ QEMU_TEST_ARGS = $(QEMU_RELEASE_ARGS) -semihosting + OPENOCD_ARG = -f /openocd/tcl/interface/ftdi/olimex-arm-usb-tiny-h.cfg -f /openocd/rpi3.cfg + JTAG_BOOT_IMAGE = jtag_boot_rpi3.img + LINKER_FILE = src/bsp/rpi/link.ld +@@ -23,23 +24,33 @@ + OUTPUT = kernel8.img + # QEMU_BINARY = qemu-system-aarch64 + # QEMU_MACHINE_TYPE = +-# QEMU_MISC_ARGS = -serial stdio -display none ++# QEMU_RELEASE_ARGS = -serial stdio -display none ++# QEMU_TEST_ARGS = $(QEMU_RELEASE_ARGS) -semihosting + OPENOCD_ARG = -f /openocd/tcl/interface/ftdi/olimex-arm-usb-tiny-h.cfg -f /openocd/rpi4.cfg + JTAG_BOOT_IMAGE = jtag_boot_rpi4.img + LINKER_FILE = src/bsp/rpi/link.ld + RUSTC_MISC_ARGS = -C target-cpu=cortex-a72 + endif + ++# Testing-specific arguments ++ifdef TEST ++ TEST_ARG = --test $(TEST) ++endif ++ ++QEMU_MISSING_STRING = "This board is not yet supported for QEMU." ++ + RUSTFLAGS = -C link-arg=-T$(LINKER_FILE) $(RUSTC_MISC_ARGS) + RUSTFLAGS_PEDANTIC = $(RUSTFLAGS) -D warnings -D missing_docs + + SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) $(wildcard **/*.ld) + +-XRUSTC_CMD = cargo xrustc \ +- --target=$(TARGET) \ ++X_CMD_ARGS = --target=$(TARGET) \ + --features bsp_$(BSP) \ + --release + ++XRUSTC_CMD = cargo xrustc $(X_CMD_ARGS) ++XTEST_CMD = cargo xtest $(X_CMD_ARGS) ++ + CARGO_OUTPUT = target/$(TARGET)/release/kernel + + OBJCOPY_CMD = cargo objcopy \ +@@ -55,12 +66,12 @@ + DOCKER_ARG_JTAG = -v $(shell pwd)/../X1_JTAG_boot:/jtag + DOCKER_ARG_NET = --network host + +-DOCKER_EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) $(QEMU_MISC_ARGS) -kernel ++DOCKER_EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) + DOCKER_EXEC_RASPBOOT = raspbootcom + DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyUSB0 + # DOCKER_EXEC_RASPBOOT_DEV = /dev/ttyACM0 + +-.PHONY: all doc qemu chainboot jtagboot openocd gdb gdb-opt0 clippy clean readelf objdump nm ++.PHONY: all doc qemu chainboot jtagboot openocd gdb gdb-opt0 clippy clean readelf objdump nm test + + all: clean $(OUTPUT) + +@@ -73,15 +84,34 @@ + + doc: + cargo xdoc --target=$(TARGET) --features bsp_$(BSP) --document-private-items +- xdg-open target/$(TARGET)/doc/kernel/index.html ++ xdg-open target/$(TARGET)/doc/libkernel/index.html + + ifeq ($(QEMU_MACHINE_TYPE),) + qemu: +- @echo "This board is not yet supported for QEMU." ++ @echo $(QEMU_MISSING_STRING) ++ ++test: ++ @echo $(QEMU_MISSING_STRING) + else + qemu: all + $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ +- $(DOCKER_EXEC_QEMU) $(OUTPUT) ++ $(DOCKER_EXEC_QEMU) $(QEMU_RELEASE_ARGS) \ ++ -kernel $(OUTPUT) ++ ++define kernel_test_runner ++ #!/usr/bin/env bash ++ ++ $(OBJCOPY_CMD) $$1 $$1.img ++ TEST_BINARY=$$(echo $$1.img | sed -e 's/.*target/target/g') ++ $(DOCKER_CMD) $(DOCKER_ARG_CURDIR) $(CONTAINER_UTILS) \ ++ ruby tests/runner.rb $(DOCKER_EXEC_QEMU) $(QEMU_TEST_ARGS) -kernel $$TEST_BINARY ++endef ++ ++test: $(SOURCES) ++ @mkdir -p target ++ $(file > target/kernel_test_runner.sh,$(kernel_test_runner)) ++ @chmod +x target/kernel_test_runner.sh ++ RUSTFLAGS="$(RUSTFLAGS_PEDANTIC)" $(XTEST_CMD) $(TEST_ARG) + endif + + chainboot: all + +diff -uNr 12_cpu_exceptions_part1/patches/Cargo.toml 13_integrated_testing/patches/Cargo.toml +--- 12_cpu_exceptions_part1/patches/Cargo.toml ++++ 13_integrated_testing/patches/Cargo.toml +@@ -0,0 +1,5 @@ ++[package] ++name = "patches" ++version = "0.1.0" ++authors = ["Andre Richter "] ++edition = "2018" + +diff -uNr 12_cpu_exceptions_part1/patches/src/lib.rs 13_integrated_testing/patches/src/lib.rs +--- 12_cpu_exceptions_part1/patches/src/lib.rs ++++ 13_integrated_testing/patches/src/lib.rs +@@ -0,0 +1,24 @@ ++// SPDX-License-Identifier: MIT OR Apache-2.0 ++// ++// Copyright (c) 2019 Andre Richter ++ ++//! Fix for register-rs. ++//! ++//! Used temporarily until https://github.com/tock/tock/issues/1482 is resolved. ++ ++#![no_std] ++ ++/// A temporary overwrite for tock's register_structs! so that it does not emit `#[test]` attributes. ++#[macro_export] ++macro_rules! register_structs { ++ { ++ $( ++ $(#[$attr:meta])* ++ $name:ident { ++ $( $fields:tt )* ++ } ++ ),* ++ } => { ++ $( register_fields!(@root $(#[$attr])* $name { $($fields)* } ); )* ++ }; ++} + +diff -uNr 12_cpu_exceptions_part1/src/arch/aarch64/exception.rs 13_integrated_testing/src/arch/aarch64/exception.rs +--- 12_cpu_exceptions_part1/src/arch/aarch64/exception.rs ++++ 13_integrated_testing/src/arch/aarch64/exception.rs +@@ -5,7 +5,7 @@ + //! Exception handling. + + use core::fmt; +-use cortex_a::{asm, barrier, regs::*}; ++use cortex_a::{barrier, regs::*}; + use register::InMemoryRegister; + + // Assembly counterpart to this file. +@@ -74,16 +74,6 @@ + /// Asynchronous exception taken from the current EL, using SP of the current EL. + #[no_mangle] + unsafe extern "C" fn current_elx_synchronous(e: &mut ExceptionContext) { +- let far_el1 = FAR_EL1.get(); +- +- // This catches the demo case for this tutorial. If the fault address happens to be 8 GiB, +- // advance the exception link register for one instruction, so that execution can continue. +- if far_el1 == 8 * 1024 * 1024 * 1024 { +- e.elr_el1 += 4; +- +- asm::eret() +- } +- + default_exception_handler(e); + } + + +diff -uNr 12_cpu_exceptions_part1/src/arch/aarch64.rs 13_integrated_testing/src/arch/aarch64.rs +--- 12_cpu_exceptions_part1/src/arch/aarch64.rs ++++ 13_integrated_testing/src/arch/aarch64.rs +@@ -155,3 +155,17 @@ + info!(" FIQ: {}", to_mask_str(exception::is_masked::())); + } + } ++ ++//-------------------------------------------------------------------------------------------------- ++// Testing ++//-------------------------------------------------------------------------------------------------- ++ ++/// Make the host QEMU binary execute `exit(1)`. ++pub fn qemu_exit_failure() -> ! { ++ qemu_exit::aarch64::exit_failure() ++} ++ ++/// Make the host QEMU binary execute `exit(0)`. ++pub fn qemu_exit_success() -> ! { ++ qemu_exit::aarch64::exit_success() ++} + +diff -uNr 12_cpu_exceptions_part1/src/arch.rs 13_integrated_testing/src/arch.rs +--- 12_cpu_exceptions_part1/src/arch.rs ++++ 13_integrated_testing/src/arch.rs +@@ -19,3 +19,21 @@ + Hypervisor, + Unknown, + } ++ ++//-------------------------------------------------------------------------------------------------- ++// Testing ++//-------------------------------------------------------------------------------------------------- ++ ++#[cfg(test)] ++mod tests { ++ use super::*; ++ use test_macros::kernel_test; ++ ++ /// Libkernel unit tests must execute in kernel mode. ++ #[kernel_test] ++ fn test_runner_executes_in_kernel_mode() { ++ let (level, _) = state::current_privilege_level(); ++ ++ assert!(level == PrivilegeLevel::Kernel) ++ } ++} + +diff -uNr 12_cpu_exceptions_part1/src/bsp/driver/bcm/bcm2xxx_gpio.rs 13_integrated_testing/src/bsp/driver/bcm/bcm2xxx_gpio.rs +--- 12_cpu_exceptions_part1/src/bsp/driver/bcm/bcm2xxx_gpio.rs ++++ 13_integrated_testing/src/bsp/driver/bcm/bcm2xxx_gpio.rs +@@ -6,7 +6,10 @@ + + use crate::{arch, arch::sync::NullLock, interface}; + use core::ops; +-use register::{mmio::ReadWrite, register_bitfields, register_structs}; ++use register::{mmio::*, register_bitfields, register_fields}; ++ ++// Temporary workaround. ++use patches::register_structs; + + // GPIO registers. + // + +diff -uNr 12_cpu_exceptions_part1/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs 13_integrated_testing/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs +--- 12_cpu_exceptions_part1/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs ++++ 13_integrated_testing/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs +@@ -6,7 +6,10 @@ + + use crate::{arch, arch::sync::NullLock, interface}; + use core::{fmt, ops}; +-use register::{mmio::*, register_bitfields, register_structs}; ++use register::{mmio::*, register_bitfields, register_fields}; ++ ++// Temporary workaround. ++use patches::register_structs; + + // PL011 UART registers. + // + +diff -uNr 12_cpu_exceptions_part1/src/bsp/rpi.rs 13_integrated_testing/src/bsp/rpi.rs +--- 12_cpu_exceptions_part1/src/bsp/rpi.rs ++++ 13_integrated_testing/src/bsp/rpi.rs +@@ -83,3 +83,13 @@ + pub fn virt_mem_layout() -> &'static KernelVirtualLayout<{ virt_mem_layout::NUM_MEM_RANGES }> { + &virt_mem_layout::LAYOUT + } ++ ++//-------------------------------------------------------------------------------------------------- ++// Testing ++//-------------------------------------------------------------------------------------------------- ++ ++/// Minimal code needed to bring up the console in QEMU (for testing only). This is often less steps ++/// than on real hardware due to QEMU's abstractions. ++/// ++/// For the RPi, nothing needs to be done. ++pub fn qemu_bring_up_console() {} + +diff -uNr 12_cpu_exceptions_part1/src/bsp.rs 13_integrated_testing/src/bsp.rs +--- 12_cpu_exceptions_part1/src/bsp.rs ++++ 13_integrated_testing/src/bsp.rs +@@ -11,3 +11,31 @@ + + #[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] + pub use rpi::*; ++ ++//-------------------------------------------------------------------------------------------------- ++// Testing ++//-------------------------------------------------------------------------------------------------- ++ ++#[cfg(test)] ++mod tests { ++ use super::*; ++ use test_macros::kernel_test; ++ ++ /// Ensure the kernel's virtual memory layout is free of overlaps. ++ #[kernel_test] ++ fn virt_mem_layout_has_no_overlaps() { ++ let layout = virt_mem_layout().inner(); ++ ++ for (i, first) in layout.iter().enumerate() { ++ for second in layout.iter().skip(i + 1) { ++ let first_range = first.virtual_range; ++ let second_range = second.virtual_range; ++ ++ assert!(!first_range().contains(second_range().start())); ++ assert!(!first_range().contains(second_range().end())); ++ assert!(!second_range().contains(first_range().start())); ++ assert!(!second_range().contains(first_range().end())); ++ } ++ } ++ } ++} + +diff -uNr 12_cpu_exceptions_part1/src/lib.rs 13_integrated_testing/src/lib.rs +--- 12_cpu_exceptions_part1/src/lib.rs ++++ 13_integrated_testing/src/lib.rs +@@ -0,0 +1,69 @@ ++// SPDX-License-Identifier: MIT OR Apache-2.0 ++// ++// Copyright (c) 2018-2019 Andre Richter ++ ++// Rust embedded logo for `make doc`. ++#![doc(html_logo_url = "https://git.io/JeGIp")] ++ ++//! The `kernel` library. ++//! ++//! Used by `main.rs` to compose the final kernel binary. ++ ++#![allow(incomplete_features)] ++#![feature(const_generics)] ++#![feature(format_args_nl)] ++#![feature(global_asm)] ++#![feature(linkage)] ++#![feature(panic_info_message)] ++#![feature(trait_alias)] ++#![no_std] ++// Testing ++#![cfg_attr(test, no_main)] ++#![feature(custom_test_frameworks)] ++#![reexport_test_harness_main = "test_main"] ++#![test_runner(crate::test_runner)] ++ ++// Conditionally includes the selected `architecture` code, which provides the `_start()` function, ++// the first function to run. ++pub mod arch; ++ ++// `_start()` then calls `runtime_init()`, which on completion, jumps to `kernel_init()`. ++mod runtime_init; ++ ++// Conditionally includes the selected `BSP` code. ++pub mod bsp; ++ ++pub mod interface; ++mod memory; ++mod panic_wait; ++pub mod print; ++ ++//-------------------------------------------------------------------------------------------------- ++// Testing ++//-------------------------------------------------------------------------------------------------- ++ ++/// The default runner for unit tests. ++pub fn test_runner(tests: &[&test_types::UnitTest]) { ++ println!("Running {} tests", tests.len()); ++ println!("-------------------------------------------------------------------\n"); ++ for (i, test) in tests.iter().enumerate() { ++ print!("{:>3}. {:.<58}", i + 1, test.name); ++ ++ // Run the actual test. ++ (test.test_func)(); ++ ++ // Failed tests call panic!(). Execution reaches here only if the test has passed. ++ println!("[ok]") ++ } ++} ++ ++/// The `kernel_init()` for unit tests. Called from `runtime_init()`. ++#[cfg(test)] ++#[no_mangle] ++unsafe fn kernel_init() -> ! { ++ bsp::qemu_bring_up_console(); ++ ++ test_main(); ++ ++ arch::qemu_exit_success() ++} + +diff -uNr 12_cpu_exceptions_part1/src/main.rs 13_integrated_testing/src/main.rs +--- 12_cpu_exceptions_part1/src/main.rs ++++ 13_integrated_testing/src/main.rs +@@ -5,7 +5,7 @@ + // Rust embedded logo for `make doc`. + #![doc(html_logo_url = "https://git.io/JeGIp")] + +-//! The `kernel` ++//! The `kernel` binary. + //! + //! The `kernel` is composed by glueing together code from + //! +@@ -19,29 +19,11 @@ + //! [Architecture-specific code]: arch/index.html + //! [`kernel::interface`]: interface/index.html + +-#![allow(incomplete_features)] +-#![feature(const_generics)] + #![feature(format_args_nl)] +-#![feature(global_asm)] +-#![feature(panic_info_message)] +-#![feature(trait_alias)] + #![no_main] + #![no_std] + +-// Conditionally includes the selected `architecture` code, which provides the `_start()` function, +-// the first function to run. +-mod arch; +- +-// `_start()` then calls `runtime_init()`, which on completion, jumps to `kernel_init()`. +-mod runtime_init; +- +-// Conditionally includes the selected `BSP` code. +-mod bsp; +- +-mod interface; +-mod memory; +-mod panic_wait; +-mod print; ++use libkernel::{arch, bsp, info, interface}; + + /// Early init code. + /// +@@ -55,6 +37,7 @@ + /// - Without it, any atomic operations, e.g. the yet-to-be-introduced spinlocks in the device + /// drivers (which currently employ NullLocks instead of spinlocks), will fail to work on + /// the RPi SoCs. ++#[no_mangle] + unsafe fn kernel_init() -> ! { + use interface::mm::MMU; + +@@ -78,8 +61,7 @@ + + /// The main function running after the early init. + fn kernel_main() -> ! { +- use core::time::Duration; +- use interface::{console::All, time::Timer}; ++ use interface::console::All; + + info!("Booting on: {}", bsp::board_name()); + +@@ -102,30 +84,6 @@ + info!(" {}. {}", i + 1, driver.compatible()); + } + +- info!("Timer test, spinning for 1 second"); +- arch::timer().spin_for(Duration::from_secs(1)); +- +- // Cause an exception by accessing a virtual address for which no translation was set up. This +- // code accesses the address 8 GiB, which is outside the mapped address space. +- // +- // For demo purposes, the exception handler will catch the faulting 8 GiB address and allow +- // execution to continue. +- info!(""); +- info!("Trying to write to address 8 GiB..."); +- let mut big_addr: u64 = 8 * 1024 * 1024 * 1024; +- unsafe { core::ptr::read_volatile(big_addr as *mut u64) }; +- +- info!("************************************************"); +- info!("Whoa! We recovered from a synchronous exception!"); +- info!("************************************************"); +- info!(""); +- info!("Let's try again"); +- +- // Now use address 9 GiB. The exception handler won't forgive us this time. +- info!("Trying to write to address 9 GiB..."); +- big_addr = 9 * 1024 * 1024 * 1024; +- unsafe { core::ptr::read_volatile(big_addr as *mut u64) }; +- + // Will never reach here in this tutorial. + info!("Echoing input now"); + loop { + +diff -uNr 12_cpu_exceptions_part1/src/memory.rs 13_integrated_testing/src/memory.rs +--- 12_cpu_exceptions_part1/src/memory.rs ++++ 13_integrated_testing/src/memory.rs +@@ -6,7 +6,6 @@ + + use core::{fmt, ops::RangeInclusive}; + +-#[allow(dead_code)] + #[derive(Copy, Clone)] + pub enum Translation { + Identity, +@@ -145,4 +144,9 @@ + info!("{}", i); + } + } ++ ++ #[cfg(test)] ++ pub fn inner(&self) -> &[RangeDescriptor; NUM_SPECIAL_RANGES] { ++ &self.inner ++ } + } + +diff -uNr 12_cpu_exceptions_part1/src/panic_wait.rs 13_integrated_testing/src/panic_wait.rs +--- 12_cpu_exceptions_part1/src/panic_wait.rs ++++ 13_integrated_testing/src/panic_wait.rs +@@ -23,6 +23,23 @@ + }) + } + ++/// The point of exit for the "standard" (non-testing) `libkernel`. ++/// ++/// This code will be used by the release kernel binary and the `integration tests`. It is linked ++/// weakly, so that the integration tests can overload it to exit `QEMU` instead of spinning ++/// forever. ++/// ++/// This is one possible approach to solve the problem that `cargo` can not know who the consumer of ++/// the library will be: ++/// - The release kernel binary that should safely park the paniced core, ++/// - or an `integration test` that is executed in QEMU, which should just exit QEMU. ++#[cfg(not(test))] ++#[linkage = "weak"] ++#[no_mangle] ++fn _panic_exit() -> ! { ++ arch::wait_forever() ++} ++ + #[panic_handler] + fn panic(info: &PanicInfo) -> ! { + if let Some(args) = info.message() { +@@ -31,5 +48,16 @@ + panic_println!("Kernel panic!"); + } + +- arch::wait_forever() ++ _panic_exit() ++} ++ ++//-------------------------------------------------------------------------------------------------- ++// Testing ++//-------------------------------------------------------------------------------------------------- ++ ++/// The point of exit when the library is compiled for testing. ++#[cfg(test)] ++#[no_mangle] ++fn _panic_exit() -> ! { ++ arch::qemu_exit_failure() + } + +diff -uNr 12_cpu_exceptions_part1/src/runtime_init.rs 13_integrated_testing/src/runtime_init.rs +--- 12_cpu_exceptions_part1/src/runtime_init.rs ++++ 13_integrated_testing/src/runtime_init.rs +@@ -15,10 +15,15 @@ + // Boundaries of the .bss section, provided by the linker script. + static mut __bss_start: u64; + static mut __bss_end: u64; ++ ++ } ++ ++ extern "Rust" { ++ fn kernel_init() -> !; + } + + // Zero out the .bss section. + r0::zero_bss(&mut __bss_start, &mut __bss_end); + +- crate::kernel_init() ++ kernel_init() + } + +diff -uNr 12_cpu_exceptions_part1/test-macros/Cargo.toml 13_integrated_testing/test-macros/Cargo.toml +--- 12_cpu_exceptions_part1/test-macros/Cargo.toml ++++ 13_integrated_testing/test-macros/Cargo.toml +@@ -0,0 +1,14 @@ ++[package] ++name = "test-macros" ++version = "0.1.0" ++authors = ["Andre Richter "] ++edition = "2018" ++ ++[lib] ++proc-macro = true ++ ++[dependencies] ++proc-macro2 = "1.x" ++quote = "1.x" ++syn = { version = "1.x", features = ["full"] } ++test-types = { path = "../test-types" } + +diff -uNr 12_cpu_exceptions_part1/test-macros/src/lib.rs 13_integrated_testing/test-macros/src/lib.rs +--- 12_cpu_exceptions_part1/test-macros/src/lib.rs ++++ 13_integrated_testing/test-macros/src/lib.rs +@@ -0,0 +1,31 @@ ++// SPDX-License-Identifier: MIT OR Apache-2.0 ++// ++// Copyright (c) 2019 Andre Richter ++ ++extern crate proc_macro; ++ ++use proc_macro::TokenStream; ++use proc_macro2::Span; ++use quote::quote; ++use syn::{parse_macro_input, Ident, ItemFn}; ++ ++#[proc_macro_attribute] ++pub fn kernel_test(_attr: TokenStream, input: TokenStream) -> TokenStream { ++ let f = parse_macro_input!(input as ItemFn); ++ ++ let test_name = &format!("{}", f.sig.ident.to_string()); ++ let test_ident = Ident::new( ++ &format!("{}_TEST_CONTAINER", f.sig.ident.to_string().to_uppercase()), ++ Span::call_site(), ++ ); ++ let test_code_block = f.block; ++ ++ quote!( ++ #[test_case] ++ const #test_ident: test_types::UnitTest = test_types::UnitTest { ++ name: #test_name, ++ test_func: || #test_code_block, ++ }; ++ ) ++ .into() ++} + +diff -uNr 12_cpu_exceptions_part1/tests/00_interface_sanity_console.rb 13_integrated_testing/tests/00_interface_sanity_console.rb +--- 12_cpu_exceptions_part1/tests/00_interface_sanity_console.rb ++++ 13_integrated_testing/tests/00_interface_sanity_console.rb +@@ -0,0 +1,50 @@ ++# frozen_string_literal: true ++ ++# SPDX-License-Identifier: MIT OR Apache-2.0 ++# ++# Copyright (c) 2019 Andre Richter ++ ++require 'expect' ++ ++TIMEOUT_SECS = 3 ++ ++# Verify sending and receiving works as expected. ++class TxRxHandshake ++ def name ++ 'Transmit and Receive handshake' ++ end ++ ++ def run(qemu_out, qemu_in) ++ qemu_in.write_nonblock('ABC') ++ raise('TX/RX test failed') if qemu_out.expect('OK1234', TIMEOUT_SECS).nil? ++ end ++end ++ ++# Check for correct TX statistics implementation. Depends on test 1 being run first. ++class TxStatistics ++ def name ++ 'Transmit statistics' ++ end ++ ++ def run(qemu_out, _qemu_in) ++ raise('chars_written reported wrong') if qemu_out.expect('6', TIMEOUT_SECS).nil? ++ end ++end ++ ++# Check for correct RX statistics implementation. Depends on test 1 being run first. ++class RxStatistics ++ def name ++ 'Receive statistics' ++ end ++ ++ def run(qemu_out, _qemu_in) ++ raise('chars_read reported wrong') if qemu_out.expect('3', TIMEOUT_SECS).nil? ++ end ++end ++ ++##-------------------------------------------------------------------------------------------------- ++## Test registration ++##-------------------------------------------------------------------------------------------------- ++def subtest_collection ++ [TxRxHandshake.new, TxStatistics.new, RxStatistics.new] ++end + +diff -uNr 12_cpu_exceptions_part1/tests/00_interface_sanity_console.rs 13_integrated_testing/tests/00_interface_sanity_console.rs +--- 12_cpu_exceptions_part1/tests/00_interface_sanity_console.rs ++++ 13_integrated_testing/tests/00_interface_sanity_console.rs +@@ -0,0 +1,33 @@ ++// SPDX-License-Identifier: MIT OR Apache-2.0 ++// ++// Copyright (c) 2019 Andre Richter ++ ++//! Console sanity tests - RX, TX and statistics. ++ ++#![feature(format_args_nl)] ++#![no_main] ++#![no_std] ++ ++mod panic_exit_failure; ++ ++use libkernel::{bsp, interface::console::*, print}; ++ ++#[no_mangle] ++unsafe fn kernel_init() -> ! { ++ bsp::qemu_bring_up_console(); ++ ++ // Handshake ++ assert_eq!(bsp::console().read_char(), 'A'); ++ assert_eq!(bsp::console().read_char(), 'B'); ++ assert_eq!(bsp::console().read_char(), 'C'); ++ print!("OK1234"); ++ ++ // 6 ++ print!("{}", bsp::console().chars_written()); ++ ++ // 3 ++ print!("{}", bsp::console().chars_read()); ++ ++ // The QEMU process running this test will be closed by the I/O test harness. ++ loop {} ++} + +diff -uNr 12_cpu_exceptions_part1/tests/01_interface_sanity_timer.rs 13_integrated_testing/tests/01_interface_sanity_timer.rs +--- 12_cpu_exceptions_part1/tests/01_interface_sanity_timer.rs ++++ 13_integrated_testing/tests/01_interface_sanity_timer.rs +@@ -0,0 +1,50 @@ ++// SPDX-License-Identifier: MIT OR Apache-2.0 ++// ++// Copyright (c) 2019 Andre Richter ++ ++//! Timer sanity tests. ++ ++#![feature(custom_test_frameworks)] ++#![no_main] ++#![no_std] ++#![reexport_test_harness_main = "test_main"] ++#![test_runner(libkernel::test_runner)] ++ ++mod panic_exit_failure; ++ ++use core::time::Duration; ++use libkernel::{arch, arch::timer, bsp, interface::time::Timer}; ++use test_macros::kernel_test; ++ ++#[no_mangle] ++unsafe fn kernel_init() -> ! { ++ bsp::qemu_bring_up_console(); ++ ++ // Depending on CPU arch, some timer bring-up code could go here. Not needed for the RPi. ++ ++ test_main(); ++ ++ arch::qemu_exit_success() ++} ++ ++/// Simple check that the timer is running. ++#[kernel_test] ++fn timer_is_counting() { ++ assert!(timer().uptime().as_nanos() > 0) ++} ++ ++/// Timer resolution must be sufficient. ++#[kernel_test] ++fn timer_resolution_is_sufficient() { ++ assert!(timer().resolution().as_nanos() < 100) ++} ++ ++/// Sanity check spin_for() implementation. ++#[kernel_test] ++fn spin_accuracy_check_1_second() { ++ let t1 = timer().uptime(); ++ timer().spin_for(Duration::from_secs(1)); ++ let t2 = timer().uptime(); ++ ++ assert_eq!((t2 - t1).as_secs(), 1) ++} + +diff -uNr 12_cpu_exceptions_part1/tests/02_arch_exception_handling.rs 13_integrated_testing/tests/02_arch_exception_handling.rs +--- 12_cpu_exceptions_part1/tests/02_arch_exception_handling.rs ++++ 13_integrated_testing/tests/02_arch_exception_handling.rs +@@ -0,0 +1,42 @@ ++// SPDX-License-Identifier: MIT OR Apache-2.0 ++// ++// Copyright (c) 2019 Andre Richter ++ ++//! Page faults must result in synchronous exceptions. ++ ++#![feature(format_args_nl)] ++#![no_main] ++#![no_std] ++ ++/// Overwrites libkernel's `panic_wait::_panic_exit()` with the QEMU-exit version. ++/// ++/// Reaching this code is a success, because it is called from the synchronous exception handler, ++/// which is what this test wants to achieve. ++/// ++/// It also means that this integration test can not use any other code that calls panic!() directly ++/// or indirectly. ++mod panic_exit_success; ++ ++use libkernel::{arch, bsp, interface::mm::MMU, println}; ++ ++#[no_mangle] ++unsafe fn kernel_init() -> ! { ++ bsp::qemu_bring_up_console(); ++ ++ println!("Testing synchronous exception handling by causing a page fault"); ++ println!("-------------------------------------------------------------------\n"); ++ ++ arch::enable_exception_handling(); ++ ++ if let Err(string) = arch::mmu().init() { ++ println!("MMU: {}", string); ++ arch::qemu_exit_failure() ++ } ++ ++ println!("Writing beyond mapped area to address 9 GiB..."); ++ let big_addr: u64 = 9 * 1024 * 1024 * 1024; ++ core::ptr::read_volatile(big_addr as *mut u64); ++ ++ // If execution reaches here, the memory access above did not cause a page fault exception. ++ arch::qemu_exit_failure() ++} + +diff -uNr 12_cpu_exceptions_part1/tests/panic_exit_failure/mod.rs 13_integrated_testing/tests/panic_exit_failure/mod.rs +--- 12_cpu_exceptions_part1/tests/panic_exit_failure/mod.rs ++++ 13_integrated_testing/tests/panic_exit_failure/mod.rs +@@ -0,0 +1,9 @@ ++// SPDX-License-Identifier: MIT OR Apache-2.0 ++// ++// Copyright (c) 2019 Andre Richter ++ ++/// Overwrites libkernel's `panic_wait::_panic_exit()` with the QEMU-exit version. ++#[no_mangle] ++fn _panic_exit() -> ! { ++ libkernel::arch::qemu_exit_failure() ++} + +diff -uNr 12_cpu_exceptions_part1/tests/panic_exit_success/mod.rs 13_integrated_testing/tests/panic_exit_success/mod.rs +--- 12_cpu_exceptions_part1/tests/panic_exit_success/mod.rs ++++ 13_integrated_testing/tests/panic_exit_success/mod.rs +@@ -0,0 +1,9 @@ ++// SPDX-License-Identifier: MIT OR Apache-2.0 ++// ++// Copyright (c) 2019 Andre Richter ++ ++/// Overwrites libkernel's `panic_wait::_panic_exit()` with the QEMU-exit version. ++#[no_mangle] ++fn _panic_exit() -> ! { ++ libkernel::arch::qemu_exit_success() ++} + +diff -uNr 12_cpu_exceptions_part1/tests/runner.rb 13_integrated_testing/tests/runner.rb +--- 12_cpu_exceptions_part1/tests/runner.rb ++++ 13_integrated_testing/tests/runner.rb +@@ -0,0 +1,137 @@ ++#!/usr/bin/env ruby ++# frozen_string_literal: true ++ ++# SPDX-License-Identifier: MIT OR Apache-2.0 ++# ++# Copyright (c) 2019 Andre Richter ++ ++require 'pty' ++ ++# Test base class. ++class Test ++ INDENT = ' ' ++ ++ def print_border(status) ++ puts ++ puts "#{INDENT}-------------------------------------------------------------------" ++ puts status ++ puts "#{INDENT}-------------------------------------------------------------------\n\n\n" ++ end ++ ++ def print_error(error) ++ puts ++ print_border("#{INDENT}❌ Failure: #{error}: #{@test_name}") ++ end ++ ++ def print_success ++ print_border("#{INDENT}✔️ Success: #{@test_name}") ++ end ++ ++ def print_output ++ puts "#{INDENT}-------------------------------------------------------------------" ++ print INDENT ++ print '🦀 ' ++ print @output.join('').gsub("\n", "\n" + INDENT) ++ end ++ ++ def finish(error) ++ print_output ++ ++ exit_code = if error ++ print_error(error) ++ false ++ else ++ print_success ++ true ++ end ++ ++ exit(exit_code) ++ end ++end ++ ++# Executes tests with console I/O. ++class ConsoleTest < Test ++ def initialize(binary, qemu_cmd, test_name, console_subtests) ++ @binary = binary ++ @qemu_cmd = qemu_cmd ++ @test_name = test_name ++ @console_subtests = console_subtests ++ @cur_subtest = 1 ++ @output = ["Running #{@console_subtests.length} console-based tests\n", ++ "-------------------------------------------------------------------\n\n"] ++ end ++ ++ def format_test_name(number, name) ++ formatted_name = number.to_s.rjust(3) + '. ' + name ++ formatted_name.ljust(63, '.') ++ end ++ ++ def run_subtest(subtest, qemu_out, qemu_in) ++ @output << format_test_name(@cur_subtest, subtest.name) ++ ++ subtest.run(qemu_out, qemu_in) ++ ++ @output << "[ok]\n" ++ @cur_subtest += 1 ++ end ++ ++ def exec ++ error = false ++ ++ PTY.spawn(@qemu_cmd) do |qemu_out, qemu_in| ++ begin ++ @console_subtests.each { |t| run_subtest(t, qemu_out, qemu_in) } ++ rescue StandardError => e ++ error = e.message ++ end ++ ++ finish(error) ++ end ++ end ++end ++ ++# A wrapper around the bare QEMU invocation. ++class RawTest < Test ++ MAX_WAIT_SECS = 5 ++ ++ def initialize(binary, qemu_cmd, test_name) ++ @binary = binary ++ @qemu_cmd = qemu_cmd ++ @test_name = test_name ++ @output = [] ++ end ++ ++ def exec ++ error = 'Timed out waiting for test' ++ io = IO.popen(@qemu_cmd) ++ ++ while IO.select([io], nil, nil, MAX_WAIT_SECS) ++ begin ++ @output << io.read_nonblock(1024) ++ rescue EOFError ++ error = false ++ break ++ end ++ end ++ ++ finish(error) ++ end ++end ++ ++##-------------------------------------------------------------------------------------------------- ++## Script entry point ++##-------------------------------------------------------------------------------------------------- ++binary = ARGV.last ++test_name = binary.gsub(modulor{.*deps/}, '').split('-')[0] ++console_test_file = 'tests/' + test_name + '.rb' ++qemu_cmd = ARGV.join(' ') ++ ++test_runner = if File.exist?(console_test_file) ++ load console_test_file ++ # subtest_collection is provided by console_test_file ++ ConsoleTest.new(binary, qemu_cmd, test_name, subtest_collection) ++ else ++ RawTest.new(binary, qemu_cmd, test_name) ++ end ++ ++test_runner.exec + +diff -uNr 12_cpu_exceptions_part1/test-types/Cargo.toml 13_integrated_testing/test-types/Cargo.toml +--- 12_cpu_exceptions_part1/test-types/Cargo.toml ++++ 13_integrated_testing/test-types/Cargo.toml +@@ -0,0 +1,5 @@ ++[package] ++name = "test-types" ++version = "0.1.0" ++authors = ["Andre Richter "] ++edition = "2018" + +diff -uNr 12_cpu_exceptions_part1/test-types/src/lib.rs 13_integrated_testing/test-types/src/lib.rs +--- 12_cpu_exceptions_part1/test-types/src/lib.rs ++++ 13_integrated_testing/test-types/src/lib.rs +@@ -0,0 +1,16 @@ ++// SPDX-License-Identifier: MIT OR Apache-2.0 ++// ++// Copyright (c) 2019 Andre Richter ++ ++//! Types for the `custom_test_frameworks` implementation. ++ ++#![no_std] ++ ++/// Unit test container. ++pub struct UnitTest { ++ /// Name of the test. ++ pub name: &'static str, ++ ++ /// Function pointer to the test. ++ pub test_func: fn(), ++} + +``` diff --git a/13_integrated_testing/kernel b/13_integrated_testing/kernel new file mode 100755 index 0000000000000000000000000000000000000000..acbae36cdc7c372311babd954f0b77508f45f38e GIT binary patch literal 152120 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"] +edition = "2018" diff --git a/13_integrated_testing/patches/src/lib.rs b/13_integrated_testing/patches/src/lib.rs new file mode 100644 index 00000000..0ea6cc15 --- /dev/null +++ b/13_integrated_testing/patches/src/lib.rs @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2019 Andre Richter + +//! Fix for register-rs. +//! +//! Used temporarily until https://github.com/tock/tock/issues/1482 is resolved. + +#![no_std] + +/// A temporary overwrite for tock's register_structs! so that it does not emit `#[test]` attributes. +#[macro_export] +macro_rules! register_structs { + { + $( + $(#[$attr:meta])* + $name:ident { + $( $fields:tt )* + } + ),* + } => { + $( register_fields!(@root $(#[$attr])* $name { $($fields)* } ); )* + }; +} diff --git a/13_integrated_testing/src/arch.rs b/13_integrated_testing/src/arch.rs new file mode 100644 index 00000000..73e49acc --- /dev/null +++ b/13_integrated_testing/src/arch.rs @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2019 Andre Richter + +//! Conditional exporting of processor architecture code. + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +mod aarch64; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +pub use aarch64::*; + +/// Architectural privilege level. +#[allow(missing_docs)] +#[derive(PartialEq)] +pub enum PrivilegeLevel { + User, + Kernel, + Hypervisor, + Unknown, +} + +//-------------------------------------------------------------------------------------------------- +// Testing +//-------------------------------------------------------------------------------------------------- + +#[cfg(test)] +mod tests { + use super::*; + use test_macros::kernel_test; + + /// Libkernel unit tests must execute in kernel mode. + #[kernel_test] + fn test_runner_executes_in_kernel_mode() { + let (level, _) = state::current_privilege_level(); + + assert!(level == PrivilegeLevel::Kernel) + } +} diff --git a/13_integrated_testing/src/arch/aarch64.rs b/13_integrated_testing/src/arch/aarch64.rs new file mode 100644 index 00000000..b2fb0b39 --- /dev/null +++ b/13_integrated_testing/src/arch/aarch64.rs @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2019 Andre Richter + +//! AArch64. + +mod exception; +mod mmu; +pub mod sync; +mod time; + +use crate::{bsp, interface}; +use cortex_a::{asm, regs::*}; + +/// The entry of the `kernel` binary. +/// +/// The function must be named `_start`, because the linker is looking for this exact name. +/// +/// # Safety +/// +/// - Linker script must ensure to place this function at `0x80_000`. +#[no_mangle] +pub unsafe extern "C" fn _start() -> ! { + const CORE_MASK: u64 = 0x3; + + // Expect the boot core to start in EL2. + if (bsp::BOOT_CORE_ID == MPIDR_EL1.get() & CORE_MASK) + && (CurrentEL.get() == CurrentEL::EL::EL2.value) + { + el2_to_el1_transition() + } else { + // If not core0, infinitely wait for events. + wait_forever() + } +} + +/// Transition from EL2 to EL1. +/// +/// # Safety +/// +/// - The HW state of EL1 must be prepared in a sound way. +/// - Exception return from EL2 must must continue execution in EL1 with ´runtime_init::init()`. +#[inline(always)] +unsafe fn el2_to_el1_transition() -> ! { + // Enable timer counter registers for EL1. + CNTHCTL_EL2.write(CNTHCTL_EL2::EL1PCEN::SET + CNTHCTL_EL2::EL1PCTEN::SET); + + // No offset for reading the counters. + CNTVOFF_EL2.set(0); + + // Set EL1 execution state to AArch64. + HCR_EL2.write(HCR_EL2::RW::EL1IsAarch64); + + // Set up a simulated exception return. + // + // First, fake a saved program status, where all interrupts were masked and SP_EL1 was used as a + // stack pointer. + SPSR_EL2.write( + SPSR_EL2::D::Masked + + SPSR_EL2::A::Masked + + SPSR_EL2::I::Masked + + SPSR_EL2::F::Masked + + SPSR_EL2::M::EL1h, + ); + + // Second, let the link register point to init(). + ELR_EL2.set(crate::runtime_init::runtime_init as *const () as u64); + + // Set up SP_EL1 (stack pointer), which will be used by EL1 once we "return" to it. + SP_EL1.set(bsp::BOOT_CORE_STACK_START); + + // Use `eret` to "return" to EL1. This will result in execution of `reset()` in EL1. + asm::eret() +} + +//-------------------------------------------------------------------------------------------------- +// Global instances +//-------------------------------------------------------------------------------------------------- + +static TIMER: time::Timer = time::Timer; +static MMU: mmu::MMU = mmu::MMU; + +//-------------------------------------------------------------------------------------------------- +// Implementation of the kernel's architecture abstraction code +//-------------------------------------------------------------------------------------------------- + +pub use asm::nop; + +/// Spin for `n` cycles. +pub fn spin_for_cycles(n: usize) { + for _ in 0..n { + asm::nop(); + } +} + +/// Return a reference to a `interface::time::TimeKeeper` implementation. +pub fn timer() -> &'static impl interface::time::Timer { + &TIMER +} + +/// Pause execution on the calling CPU core. +#[inline(always)] +pub fn wait_forever() -> ! { + loop { + asm::wfe() + } +} + +/// Enable exception handling. +/// +/// # Safety +/// +/// - Changes the HW state of the processing element. +pub unsafe fn enable_exception_handling() { + exception::set_vbar_el1(); +} + +/// Return a reference to an `interface::mm::MMU` implementation. +pub fn mmu() -> &'static impl interface::mm::MMU { + &MMU +} + +/// Information about the HW state. +pub mod state { + use crate::arch::PrivilegeLevel; + use cortex_a::regs::*; + + /// The processing element's current privilege level. + pub fn current_privilege_level() -> (PrivilegeLevel, &'static str) { + let el = CurrentEL.read_as_enum(CurrentEL::EL); + match el { + Some(CurrentEL::EL::Value::EL2) => (PrivilegeLevel::Hypervisor, "EL2"), + Some(CurrentEL::EL::Value::EL1) => (PrivilegeLevel::Kernel, "EL1"), + Some(CurrentEL::EL::Value::EL0) => (PrivilegeLevel::User, "EL0"), + _ => (PrivilegeLevel::Unknown, "Unknown"), + } + } + + /// Print the AArch64 exceptions status. + #[rustfmt::skip] + pub fn print_exception_state() { + use super::{ + exception, + exception::{Debug, SError, FIQ, IRQ}, + }; + use crate::info; + + let to_mask_str = |x| -> _ { + if x { "Masked" } else { "Unmasked" } + }; + + info!(" Debug: {}", to_mask_str(exception::is_masked::())); + info!(" SError: {}", to_mask_str(exception::is_masked::())); + info!(" IRQ: {}", to_mask_str(exception::is_masked::())); + info!(" FIQ: {}", to_mask_str(exception::is_masked::())); + } +} + +//-------------------------------------------------------------------------------------------------- +// Testing +//-------------------------------------------------------------------------------------------------- + +/// Make the host QEMU binary execute `exit(1)`. +pub fn qemu_exit_failure() -> ! { + qemu_exit::aarch64::exit_failure() +} + +/// Make the host QEMU binary execute `exit(0)`. +pub fn qemu_exit_success() -> ! { + qemu_exit::aarch64::exit_success() +} diff --git a/13_integrated_testing/src/arch/aarch64/exception.S b/13_integrated_testing/src/arch/aarch64/exception.S new file mode 100644 index 00000000..3ed770b2 --- /dev/null +++ b/13_integrated_testing/src/arch/aarch64/exception.S @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2019 Andre Richter + +/// Call the function provided by parameter `\handler` after saving exception context. Provide the +/// context as the first parameter to '\handler'. +.macro CALL_WITH_CONTEXT handler + // Make room on the stack for the exception context. + sub sp, sp, #16 * 17 + + // Store all general purpose registers on the stack. + stp x0, x1, [sp, #16 * 0] + stp x2, x3, [sp, #16 * 1] + stp x4, x5, [sp, #16 * 2] + stp x6, x7, [sp, #16 * 3] + stp x8, x9, [sp, #16 * 4] + stp x10, x11, [sp, #16 * 5] + stp x12, x13, [sp, #16 * 6] + stp x14, x15, [sp, #16 * 7] + stp x16, x17, [sp, #16 * 8] + stp x18, x19, [sp, #16 * 9] + stp x20, x21, [sp, #16 * 10] + stp x22, x23, [sp, #16 * 11] + stp x24, x25, [sp, #16 * 12] + stp x26, x27, [sp, #16 * 13] + stp x28, x29, [sp, #16 * 14] + + // Add the exception link register (ELR_EL1) and the saved program status (SPSR_EL1). + mrs x1, ELR_EL1 + mrs x2, SPSR_EL1 + + stp lr, x1, [sp, #16 * 15] + str w2, [sp, #16 * 16] + + // x0 is the first argument for the function called through `\handler`. + mov x0, sp + + // Call `\handler`. + bl \handler + + // After returning from exception handling code, replay the saved context and return via `eret`. + b __exception_restore_context +.endm + +.macro FIQ_SUSPEND +1: wfe + b 1b +.endm + +//-------------------------------------------------------------------------------------------------- +// The exception vector table. +//-------------------------------------------------------------------------------------------------- +.section .exception_vectors, "ax", @progbits + +// Align by 2^11 bytes, as demanded by the AArch64 spec. Same as ALIGN(2048) in an ld script. +.align 11 + +// Export a symbol for the Rust code to use. +__exception_vector_start: + +// Current exception level with SP_EL0. +// .org sets the offset relative to section start. +// +// It must be ensured that `CALL_WITH_CONTEXT` <= 0x80 bytes. +.org 0x000 + CALL_WITH_CONTEXT current_el0_synchronous +.org 0x080 + CALL_WITH_CONTEXT current_el0_irq +.org 0x100 + FIQ_SUSPEND +.org 0x180 + CALL_WITH_CONTEXT current_el0_serror + +// Current exception level with SP_ELx, x > 0. +.org 0x200 + CALL_WITH_CONTEXT current_elx_synchronous +.org 0x280 + CALL_WITH_CONTEXT current_elx_irq +.org 0x300 + FIQ_SUSPEND +.org 0x380 + CALL_WITH_CONTEXT current_elx_serror + +// Lower exception level, aarch64 +.org 0x400 + CALL_WITH_CONTEXT lower_aarch64_synchronous +.org 0x480 + CALL_WITH_CONTEXT lower_aarch64_irq +.org 0x500 + FIQ_SUSPEND +.org 0x580 + CALL_WITH_CONTEXT lower_aarch64_serror + +// Lower exception level, aarch32 +.org 0x600 + CALL_WITH_CONTEXT lower_aarch32_synchronous +.org 0x680 + CALL_WITH_CONTEXT lower_aarch32_irq +.org 0x700 + FIQ_SUSPEND +.org 0x780 + CALL_WITH_CONTEXT lower_aarch32_serror +.org 0x800 + +//-------------------------------------------------------------------------------------------------- +// Helper functions +//-------------------------------------------------------------------------------------------------- +__exception_restore_context: + ldr w19, [sp, #16 * 16] + ldp lr, x20, [sp, #16 * 15] + + msr SPSR_EL1, x19 + msr ELR_EL1, x20 + + ldp x0, x1, [sp, #16 * 0] + ldp x2, x3, [sp, #16 * 1] + ldp x4, x5, [sp, #16 * 2] + ldp x6, x7, [sp, #16 * 3] + ldp x8, x9, [sp, #16 * 4] + ldp x10, x11, [sp, #16 * 5] + ldp x12, x13, [sp, #16 * 6] + ldp x14, x15, [sp, #16 * 7] + ldp x16, x17, [sp, #16 * 8] + ldp x18, x19, [sp, #16 * 9] + ldp x20, x21, [sp, #16 * 10] + ldp x22, x23, [sp, #16 * 11] + ldp x24, x25, [sp, #16 * 12] + ldp x26, x27, [sp, #16 * 13] + ldp x28, x29, [sp, #16 * 14] + + add sp, sp, #16 * 17 + + eret diff --git a/13_integrated_testing/src/arch/aarch64/exception.rs b/13_integrated_testing/src/arch/aarch64/exception.rs new file mode 100644 index 00000000..fe2551ad --- /dev/null +++ b/13_integrated_testing/src/arch/aarch64/exception.rs @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2019 Andre Richter + +//! Exception handling. + +use core::fmt; +use cortex_a::{barrier, regs::*}; +use register::InMemoryRegister; + +// Assembly counterpart to this file. +global_asm!(include_str!("exception.S")); + +/// Wrapper struct for memory copy of SPSR_EL1. +#[repr(transparent)] +struct SpsrEL1(InMemoryRegister); + +/// The exception context as it is stored on the stack on exception entry. +#[repr(C)] +struct ExceptionContext { + // General Purpose Registers. + gpr: [u64; 30], + // The link register, aka x30. + lr: u64, + // Exception link register. The program counter at the time the exception happened. + elr_el1: u64, + // Saved program status. + spsr_el1: SpsrEL1, +} + +/// Wrapper struct for pretty printing ESR_EL1. +struct EsrEL1; + +//-------------------------------------------------------------------------------------------------- +// Exception vector implementation +//-------------------------------------------------------------------------------------------------- + +/// Print verbose information about the exception and the panic. +fn default_exception_handler(e: &ExceptionContext) { + panic!( + "\n\nCPU Exception!\n\ + FAR_EL1: {:#018x}\n\ + {}\n\ + {}", + FAR_EL1.get(), + EsrEL1 {}, + e + ); +} + +//-------------------------------------------------------------------------------------------------- +// Current, EL0 +//-------------------------------------------------------------------------------------------------- + +#[no_mangle] +unsafe extern "C" fn current_el0_synchronous(e: &mut ExceptionContext) { + default_exception_handler(e); +} + +#[no_mangle] +unsafe extern "C" fn current_el0_irq(e: &mut ExceptionContext) { + default_exception_handler(e); +} + +#[no_mangle] +unsafe extern "C" fn current_el0_serror(e: &mut ExceptionContext) { + default_exception_handler(e); +} + +//-------------------------------------------------------------------------------------------------- +// Current, ELx +//-------------------------------------------------------------------------------------------------- + +/// Asynchronous exception taken from the current EL, using SP of the current EL. +#[no_mangle] +unsafe extern "C" fn current_elx_synchronous(e: &mut ExceptionContext) { + default_exception_handler(e); +} + +#[no_mangle] +unsafe extern "C" fn current_elx_irq(e: &mut ExceptionContext) { + default_exception_handler(e); +} + +#[no_mangle] +unsafe extern "C" fn current_elx_serror(e: &mut ExceptionContext) { + default_exception_handler(e); +} + +//-------------------------------------------------------------------------------------------------- +// Lower, AArch64 +//-------------------------------------------------------------------------------------------------- + +#[no_mangle] +unsafe extern "C" fn lower_aarch64_synchronous(e: &mut ExceptionContext) { + default_exception_handler(e); +} + +#[no_mangle] +unsafe extern "C" fn lower_aarch64_irq(e: &mut ExceptionContext) { + default_exception_handler(e); +} + +#[no_mangle] +unsafe extern "C" fn lower_aarch64_serror(e: &mut ExceptionContext) { + default_exception_handler(e); +} + +//-------------------------------------------------------------------------------------------------- +// Lower, AArch32 +//-------------------------------------------------------------------------------------------------- + +#[no_mangle] +unsafe extern "C" fn lower_aarch32_synchronous(e: &mut ExceptionContext) { + default_exception_handler(e); +} + +#[no_mangle] +unsafe extern "C" fn lower_aarch32_irq(e: &mut ExceptionContext) { + default_exception_handler(e); +} + +#[no_mangle] +unsafe extern "C" fn lower_aarch32_serror(e: &mut ExceptionContext) { + default_exception_handler(e); +} + +//-------------------------------------------------------------------------------------------------- +// Pretty printing +//-------------------------------------------------------------------------------------------------- + +/// Human readable ESR_EL1. +#[rustfmt::skip] +impl fmt::Display for EsrEL1 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let esr_el1 = ESR_EL1.extract(); + + // Raw print of whole register. + writeln!(f, "ESR_EL1: {:#010x}", esr_el1.get())?; + + // Raw print of exception class. + write!(f, " Exception Class (EC) : {:#x}", esr_el1.read(ESR_EL1::EC))?; + + // Exception class, translation. + let ec_translation = match esr_el1.read_as_enum(ESR_EL1::EC) { + Some(ESR_EL1::EC::Value::DataAbortCurrentEL) => "Data Abort, current EL", + _ => "N/A", + }; + writeln!(f, " - {}", ec_translation)?; + + // Raw print of instruction specific syndrome. + write!(f, " Instr Specific Syndrome (ISS): {:#x}", esr_el1.read(ESR_EL1::ISS))?; + + Ok(()) + } +} + +/// Human readable SPSR_EL1. +#[rustfmt::skip] +impl fmt::Display for SpsrEL1 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // Raw value. + writeln!(f, "SPSR_EL1: {:#010x}", self.0.get())?; + + let to_flag_str = |x| -> _ { + if x { "Set" } else { "Not set" } + }; + + writeln!(f, " Flags:")?; + writeln!(f, " Negative (N): {}", to_flag_str(self.0.is_set(SPSR_EL1::N)))?; + writeln!(f, " Zero (Z): {}", to_flag_str(self.0.is_set(SPSR_EL1::Z)))?; + writeln!(f, " Carry (C): {}", to_flag_str(self.0.is_set(SPSR_EL1::C)))?; + writeln!(f, " Overflow (V): {}", to_flag_str(self.0.is_set(SPSR_EL1::V)))?; + + let to_mask_str = |x| -> _ { + if x { "Masked" } else { "Unmasked" } + }; + + writeln!(f, " Exception handling state:")?; + writeln!(f, " Debug (D): {}", to_mask_str(self.0.is_set(SPSR_EL1::D)))?; + writeln!(f, " SError (A): {}", to_mask_str(self.0.is_set(SPSR_EL1::A)))?; + writeln!(f, " IRQ (I): {}", to_mask_str(self.0.is_set(SPSR_EL1::I)))?; + writeln!(f, " FIQ (F): {}", to_mask_str(self.0.is_set(SPSR_EL1::F)))?; + + write!(f, " Illegal Execution State (IL): {}", + to_flag_str(self.0.is_set(SPSR_EL1::IL)) + )?; + + Ok(()) + } +} + +/// Human readable print of the exception context. +impl fmt::Display for ExceptionContext { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + writeln!(f, "ELR_EL1: {:#018x}", self.elr_el1)?; + writeln!(f, "{}", self.spsr_el1)?; + writeln!(f)?; + writeln!(f, "General purpose register:")?; + + #[rustfmt::skip] + let alternating = |x| -> _ { + if x % 2 == 0 { " " } else { "\n" } + }; + + // Print two registers per line. + for (i, reg) in self.gpr.iter().enumerate() { + write!(f, " x{: <2}: {: >#018x}{}", i, reg, alternating(i))?; + } + write!(f, " lr : {:#018x}", self.lr)?; + + Ok(()) + } +} + +//-------------------------------------------------------------------------------------------------- +// Arch-public +//-------------------------------------------------------------------------------------------------- + +/// Set the exception vector base address register. +/// +/// # Safety +/// +/// - The vector table and the symbol `__exception_vector_table_start` from the linker script must +/// adhere to the alignment and size constraints demanded by the AArch64 spec. +pub unsafe fn set_vbar_el1() { + // Provided by exception.S. + extern "C" { + static mut __exception_vector_start: u64; + } + let addr: u64 = &__exception_vector_start as *const _ as u64; + + VBAR_EL1.set(addr); + + // Force VBAR update to complete before next instruction. + barrier::isb(barrier::SY); +} + +pub trait DaifField { + fn daif_field() -> register::Field; +} + +pub struct Debug; +pub struct SError; +pub struct IRQ; +pub struct FIQ; + +impl DaifField for Debug { + fn daif_field() -> register::Field { + DAIF::D + } +} + +impl DaifField for SError { + fn daif_field() -> register::Field { + DAIF::A + } +} + +impl DaifField for IRQ { + fn daif_field() -> register::Field { + DAIF::I + } +} + +impl DaifField for FIQ { + fn daif_field() -> register::Field { + DAIF::F + } +} + +pub fn is_masked() -> bool { + DAIF.is_set(T::daif_field()) +} diff --git a/13_integrated_testing/src/arch/aarch64/mmu.rs b/13_integrated_testing/src/arch/aarch64/mmu.rs new file mode 100644 index 00000000..0c0c9202 --- /dev/null +++ b/13_integrated_testing/src/arch/aarch64/mmu.rs @@ -0,0 +1,300 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2019 Andre Richter + +//! Memory Management Unit. +//! +//! Static page tables, compiled on boot; Everything 64 KiB granule. + +use crate::{ + bsp, interface, + memory::{AccessPermissions, AttributeFields, MemAttributes}, +}; +use core::convert; +use cortex_a::{barrier, regs::*}; +use register::register_bitfields; + +// A table descriptor, as per AArch64 Reference Manual Figure D4-15. +register_bitfields! {u64, + STAGE1_TABLE_DESCRIPTOR [ + /// Physical address of the next page table. + NEXT_LEVEL_TABLE_ADDR_64KiB OFFSET(16) NUMBITS(32) [], // [47:16] + + TYPE OFFSET(1) NUMBITS(1) [ + Block = 0, + Table = 1 + ], + + VALID OFFSET(0) NUMBITS(1) [ + False = 0, + True = 1 + ] + ] +} + +// A level 3 page descriptor, as per AArch64 Reference Manual Figure D4-17. +register_bitfields! {u64, + STAGE1_PAGE_DESCRIPTOR [ + /// Privileged execute-never. + PXN OFFSET(53) NUMBITS(1) [ + False = 0, + True = 1 + ], + + /// Physical address of the next page table (lvl2) or the page descriptor (lvl3). + OUTPUT_ADDR_64KiB OFFSET(16) NUMBITS(32) [], // [47:16] + + /// Access flag. + AF OFFSET(10) NUMBITS(1) [ + False = 0, + True = 1 + ], + + /// Shareability field. + SH OFFSET(8) NUMBITS(2) [ + OuterShareable = 0b10, + InnerShareable = 0b11 + ], + + /// Access Permissions. + AP OFFSET(6) NUMBITS(2) [ + RW_EL1 = 0b00, + RW_EL1_EL0 = 0b01, + RO_EL1 = 0b10, + RO_EL1_EL0 = 0b11 + ], + + /// Memory attributes index into the MAIR_EL1 register. + AttrIndx OFFSET(2) NUMBITS(3) [], + + TYPE OFFSET(1) NUMBITS(1) [ + Block = 0, + Table = 1 + ], + + VALID OFFSET(0) NUMBITS(1) [ + False = 0, + True = 1 + ] + ] +} + +const SIXTYFOUR_KIB_SHIFT: usize = 16; // log2(64 * 1024) +const FIVETWELVE_MIB_SHIFT: usize = 29; // log2(512 * 1024 * 1024) + +/// A table descriptor for 64 KiB aperture. +/// +/// The output points to the next table. +#[derive(Copy, Clone)] +#[repr(transparent)] +struct TableDescriptor(u64); + +/// A page descriptor with 64 KiB aperture. +/// +/// The output points to physical memory. +#[derive(Copy, Clone)] +#[repr(transparent)] +struct PageDescriptor(u64); + +/// Big monolithic struct for storing the page tables. Individual levels must be 64 KiB aligned, +/// hence the "reverse" order of appearance. +#[repr(C)] +#[repr(align(65536))] +struct PageTables { + // Page descriptors, covering 64 KiB windows per entry. + lvl3: [[PageDescriptor; 8192]; N], + // Table descriptors, covering 512 MiB windows. + lvl2: [TableDescriptor; N], +} + +/// Usually evaluates to 1 GiB for RPi3 and 4 GiB for RPi 4. +const ENTRIES_512_MIB: usize = bsp::addr_space_size() >> FIVETWELVE_MIB_SHIFT; + +/// The page tables. +/// +/// Supposed to land in `.bss`. Therefore, ensure that they boil down to all "0" entries. +static mut TABLES: PageTables<{ ENTRIES_512_MIB }> = PageTables { + lvl3: [[PageDescriptor(0); 8192]; ENTRIES_512_MIB], + lvl2: [TableDescriptor(0); ENTRIES_512_MIB], +}; + +trait BaseAddr { + fn base_addr_u64(&self) -> u64; + fn base_addr_usize(&self) -> usize; +} + +impl BaseAddr for [T; N] { + fn base_addr_u64(&self) -> u64 { + self as *const T as u64 + } + + fn base_addr_usize(&self) -> usize { + self as *const T as usize + } +} + +impl convert::From for TableDescriptor { + fn from(next_lvl_table_addr: usize) -> Self { + let shifted = next_lvl_table_addr >> SIXTYFOUR_KIB_SHIFT; + let val = (STAGE1_TABLE_DESCRIPTOR::VALID::True + + STAGE1_TABLE_DESCRIPTOR::TYPE::Table + + STAGE1_TABLE_DESCRIPTOR::NEXT_LEVEL_TABLE_ADDR_64KiB.val(shifted as u64)) + .value; + + TableDescriptor(val) + } +} + +/// Convert the kernel's generic memory range attributes to HW-specific attributes of the MMU. +impl convert::From + for register::FieldValue +{ + fn from(attribute_fields: AttributeFields) -> Self { + // Memory attributes. + let mut desc = match attribute_fields.mem_attributes { + MemAttributes::CacheableDRAM => { + STAGE1_PAGE_DESCRIPTOR::SH::InnerShareable + + STAGE1_PAGE_DESCRIPTOR::AttrIndx.val(mair::NORMAL) + } + MemAttributes::Device => { + STAGE1_PAGE_DESCRIPTOR::SH::OuterShareable + + STAGE1_PAGE_DESCRIPTOR::AttrIndx.val(mair::DEVICE) + } + }; + + // Access Permissions. + desc += match attribute_fields.acc_perms { + AccessPermissions::ReadOnly => STAGE1_PAGE_DESCRIPTOR::AP::RO_EL1, + AccessPermissions::ReadWrite => STAGE1_PAGE_DESCRIPTOR::AP::RW_EL1, + }; + + // Execute Never. + desc += if attribute_fields.execute_never { + STAGE1_PAGE_DESCRIPTOR::PXN::True + } else { + STAGE1_PAGE_DESCRIPTOR::PXN::False + }; + + desc + } +} + +impl PageDescriptor { + fn new(output_addr: usize, attribute_fields: AttributeFields) -> PageDescriptor { + let shifted = output_addr >> SIXTYFOUR_KIB_SHIFT; + let val = (STAGE1_PAGE_DESCRIPTOR::VALID::True + + STAGE1_PAGE_DESCRIPTOR::AF::True + + attribute_fields.into() + + STAGE1_PAGE_DESCRIPTOR::TYPE::Table + + STAGE1_PAGE_DESCRIPTOR::OUTPUT_ADDR_64KiB.val(shifted as u64)) + .value; + + PageDescriptor(val) + } +} + +/// Constants for indexing the MAIR_EL1. +#[allow(dead_code)] +mod mair { + pub const DEVICE: u64 = 0; + pub const NORMAL: u64 = 1; +} + +/// Setup function for the MAIR_EL1 register. +fn set_up_mair() { + // Define the memory types being mapped. + MAIR_EL1.write( + // Attribute 1 - Cacheable normal DRAM. + MAIR_EL1::Attr1_HIGH::Memory_OuterWriteBack_NonTransient_ReadAlloc_WriteAlloc + + MAIR_EL1::Attr1_LOW_MEMORY::InnerWriteBack_NonTransient_ReadAlloc_WriteAlloc + + // Attribute 0 - Device. + + MAIR_EL1::Attr0_HIGH::Device + + MAIR_EL1::Attr0_LOW_DEVICE::Device_nGnRE, + ); +} + +/// Iterates over all static page table entries and fills them at once. +/// +/// # Safety +/// +/// - Modifies a `static mut`. Ensure it only happens from here. +unsafe fn populate_pt_entries() -> Result<(), &'static str> { + for (l2_nr, l2_entry) in TABLES.lvl2.iter_mut().enumerate() { + *l2_entry = TABLES.lvl3[l2_nr].base_addr_usize().into(); + + for (l3_nr, l3_entry) in TABLES.lvl3[l2_nr].iter_mut().enumerate() { + let virt_addr = (l2_nr << FIVETWELVE_MIB_SHIFT) + (l3_nr << SIXTYFOUR_KIB_SHIFT); + + let (output_addr, attribute_fields) = + bsp::virt_mem_layout().get_virt_addr_properties(virt_addr)?; + + *l3_entry = PageDescriptor::new(output_addr, attribute_fields); + } + } + + Ok(()) +} + +/// Configure various settings of stage 1 of the EL1 translation regime. +fn configure_translation_control() { + let ips = ID_AA64MMFR0_EL1.read(ID_AA64MMFR0_EL1::PARange); + TCR_EL1.write( + TCR_EL1::TBI0::Ignored + + TCR_EL1::IPS.val(ips) + + TCR_EL1::TG0::KiB_64 + + TCR_EL1::SH0::Inner + + TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + + TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + + TCR_EL1::EPD0::EnableTTBR0Walks + + TCR_EL1::T0SZ.val(32), // TTBR0 spans 4 GiB total. + ); +} + +//-------------------------------------------------------------------------------------------------- +// Arch-public +//-------------------------------------------------------------------------------------------------- + +pub struct MMU; + +//-------------------------------------------------------------------------------------------------- +// OS interface implementations +//-------------------------------------------------------------------------------------------------- + +impl interface::mm::MMU for MMU { + /// Compile the page tables from the `BSP`-supplied `virt_mem_layout()`. + /// + /// # Safety + /// + /// - User must ensure that the hardware supports the paremeters being set here. + unsafe fn init(&self) -> Result<(), &'static str> { + // Fail early if translation granule is not supported. Both RPis support it, though. + if !ID_AA64MMFR0_EL1.matches_all(ID_AA64MMFR0_EL1::TGran64::Supported) { + return Err("64 KiB translation granule not supported"); + } + + // Prepare the memory attribute indirection register. + set_up_mair(); + + // Populate page tables. + populate_pt_entries()?; + + // Set the "Translation Table Base Register". + TTBR0_EL1.set_baddr(TABLES.lvl2.base_addr_u64()); + + configure_translation_control(); + + // Switch the MMU on. + // + // First, force all previous changes to be seen before the MMU is enabled. + barrier::isb(barrier::SY); + + // Enable the MMU and turn on data and instruction caching. + SCTLR_EL1.modify(SCTLR_EL1::M::Enable + SCTLR_EL1::C::Cacheable + SCTLR_EL1::I::Cacheable); + + // Force MMU init to complete before next instruction. + barrier::isb(barrier::SY); + + Ok(()) + } +} diff --git a/13_integrated_testing/src/arch/aarch64/sync.rs b/13_integrated_testing/src/arch/aarch64/sync.rs new file mode 100644 index 00000000..7e8781e6 --- /dev/null +++ b/13_integrated_testing/src/arch/aarch64/sync.rs @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2019 Andre Richter + +//! Synchronization primitives. + +use crate::interface; +use core::cell::UnsafeCell; + +//-------------------------------------------------------------------------------------------------- +// Arch-public +//-------------------------------------------------------------------------------------------------- + +/// A pseudo-lock for teaching purposes. +/// +/// Used to introduce [interior mutability]. +/// +/// In contrast to a real Mutex implementation, does not protect against concurrent access to the +/// contained data. This part is preserved for later lessons. +/// +/// The lock will only be used as long as it is safe to do so, i.e. as long as the kernel is +/// executing single-threaded, aka only running on a single core with interrupts disabled. +/// +/// [interior mutability]: https://doc.rust-lang.org/std/cell/index.html +pub struct NullLock { + data: UnsafeCell, +} + +unsafe impl Send for NullLock {} +unsafe impl Sync for NullLock {} + +impl NullLock { + /// Wraps `data` into a new `NullLock`. + pub const fn new(data: T) -> NullLock { + NullLock { + data: UnsafeCell::new(data), + } + } +} + +//-------------------------------------------------------------------------------------------------- +// OS interface implementations +//-------------------------------------------------------------------------------------------------- + +impl interface::sync::Mutex for &NullLock { + type Data = T; + + fn lock(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R { + // In a real lock, there would be code encapsulating this line that ensures that this + // mutable reference will ever only be given out once at a time. + f(unsafe { &mut *self.data.get() }) + } +} diff --git a/13_integrated_testing/src/arch/aarch64/time.rs b/13_integrated_testing/src/arch/aarch64/time.rs new file mode 100644 index 00000000..d4fa88f1 --- /dev/null +++ b/13_integrated_testing/src/arch/aarch64/time.rs @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2019 Andre Richter + +//! Timer primitives. + +use crate::{interface, warn}; +use core::time::Duration; +use cortex_a::regs::*; + +const NS_PER_S: u64 = 1_000_000_000; + +//-------------------------------------------------------------------------------------------------- +// Arch-public +//-------------------------------------------------------------------------------------------------- + +pub struct Timer; + +//-------------------------------------------------------------------------------------------------- +// OS interface implementations +//-------------------------------------------------------------------------------------------------- + +impl interface::time::Timer for Timer { + fn resolution(&self) -> Duration { + Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64)) + } + + fn uptime(&self) -> Duration { + let frq: u64 = CNTFRQ_EL0.get() as u64; + let current_count: u64 = CNTPCT_EL0.get() * NS_PER_S; + + Duration::from_nanos(current_count / frq) + } + + fn spin_for(&self, duration: Duration) { + // Instantly return on zero. + if duration.as_nanos() == 0 { + return; + } + + // Calculate the register compare value. + let frq = CNTFRQ_EL0.get() as u64; + let x = match frq.checked_mul(duration.as_nanos() as u64) { + None => { + warn!("Spin duration too long, skipping"); + return; + } + Some(val) => val, + }; + let tval = x / NS_PER_S; + + // Check if it is within supported bounds. + let warn: Option<&str> = if tval == 0 { + Some("smaller") + } else if tval > u32::max_value().into() { + Some("bigger") + } else { + None + }; + + if let Some(w) = warn { + warn!( + "Spin duration {} than architecturally supported, skipping", + w + ); + return; + } + + // Set the compare value register. + CNTP_TVAL_EL0.set(tval as u32); + + // Kick off the counting. // Disable timer interrupt. + CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET); + + // ISTATUS will be '1' when cval ticks have passed. Busy-check it. + while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {} + + // Disable counting again. + CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR); + } +} diff --git a/13_integrated_testing/src/bsp.rs b/13_integrated_testing/src/bsp.rs new file mode 100644 index 00000000..01856311 --- /dev/null +++ b/13_integrated_testing/src/bsp.rs @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2019 Andre Richter + +//! Conditional exporting of Board Support Packages. + +mod driver; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +mod rpi; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +pub use rpi::*; + +//-------------------------------------------------------------------------------------------------- +// Testing +//-------------------------------------------------------------------------------------------------- + +#[cfg(test)] +mod tests { + use super::*; + use test_macros::kernel_test; + + /// Ensure the kernel's virtual memory layout is free of overlaps. + #[kernel_test] + fn virt_mem_layout_has_no_overlaps() { + let layout = virt_mem_layout().inner(); + + for (i, first) in layout.iter().enumerate() { + for second in layout.iter().skip(i + 1) { + let first_range = first.virtual_range; + let second_range = second.virtual_range; + + assert!(!first_range().contains(second_range().start())); + assert!(!first_range().contains(second_range().end())); + assert!(!second_range().contains(first_range().start())); + assert!(!second_range().contains(first_range().end())); + } + } + } +} diff --git a/13_integrated_testing/src/bsp/driver.rs b/13_integrated_testing/src/bsp/driver.rs new file mode 100644 index 00000000..2c52ae61 --- /dev/null +++ b/13_integrated_testing/src/bsp/driver.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2019 Andre Richter + +//! Drivers. + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +mod bcm; + +#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))] +pub use bcm::*; diff --git a/13_integrated_testing/src/bsp/driver/bcm.rs b/13_integrated_testing/src/bsp/driver/bcm.rs new file mode 100644 index 00000000..33383276 --- /dev/null +++ b/13_integrated_testing/src/bsp/driver/bcm.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2019 Andre Richter + +//! BCM driver top level. + +mod bcm2xxx_gpio; +mod bcm2xxx_pl011_uart; + +pub use bcm2xxx_gpio::GPIO; +pub use bcm2xxx_pl011_uart::{PL011Uart, PanicUart}; diff --git a/13_integrated_testing/src/bsp/driver/bcm/bcm2xxx_gpio.rs b/13_integrated_testing/src/bsp/driver/bcm/bcm2xxx_gpio.rs new file mode 100644 index 00000000..f3860fe6 --- /dev/null +++ b/13_integrated_testing/src/bsp/driver/bcm/bcm2xxx_gpio.rs @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2019 Andre Richter + +//! GPIO driver. + +use crate::{arch, arch::sync::NullLock, interface}; +use core::ops; +use register::{mmio::*, register_bitfields, register_fields}; + +// Temporary workaround. +use patches::register_structs; + +// GPIO registers. +// +// Descriptions taken from +// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf +register_bitfields! { + u32, + + /// GPIO Function Select 1 + GPFSEL1 [ + /// Pin 15 + FSEL15 OFFSET(15) NUMBITS(3) [ + Input = 0b000, + Output = 0b001, + AltFunc0 = 0b100 // PL011 UART RX + + ], + + /// Pin 14 + FSEL14 OFFSET(12) NUMBITS(3) [ + Input = 0b000, + Output = 0b001, + AltFunc0 = 0b100 // PL011 UART TX + ] + ], + + /// GPIO Pull-up/down Clock Register 0 + GPPUDCLK0 [ + /// Pin 15 + PUDCLK15 OFFSET(15) NUMBITS(1) [ + NoEffect = 0, + AssertClock = 1 + ], + + /// Pin 14 + PUDCLK14 OFFSET(14) NUMBITS(1) [ + NoEffect = 0, + AssertClock = 1 + ] + ] +} + +register_structs! { + #[allow(non_snake_case)] + RegisterBlock { + (0x00 => GPFSEL0: ReadWrite), + (0x04 => GPFSEL1: ReadWrite), + (0x08 => GPFSEL2: ReadWrite), + (0x0C => GPFSEL3: ReadWrite), + (0x10 => GPFSEL4: ReadWrite), + (0x14 => GPFSEL5: ReadWrite), + (0x18 => _reserved1), + (0x94 => GPPUD: ReadWrite), + (0x98 => GPPUDCLK0: ReadWrite), + (0x9C => GPPUDCLK1: ReadWrite), + (0xA0 => @END), + } +} + +/// The driver's private data. +struct GPIOInner { + base_addr: usize, +} + +/// Deref to RegisterBlock. +impl ops::Deref for GPIOInner { + type Target = RegisterBlock; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.ptr() } + } +} + +impl GPIOInner { + const fn new(base_addr: usize) -> GPIOInner { + GPIOInner { base_addr } + } + + /// Return a pointer to the register block. + fn ptr(&self) -> *const RegisterBlock { + self.base_addr as *const _ + } +} + +//-------------------------------------------------------------------------------------------------- +// BSP-public +//-------------------------------------------------------------------------------------------------- +use interface::sync::Mutex; + +/// The driver's main struct. +pub struct GPIO { + inner: NullLock, +} + +impl GPIO { + pub const unsafe fn new(base_addr: usize) -> GPIO { + GPIO { + inner: NullLock::new(GPIOInner::new(base_addr)), + } + } + + /// Map PL011 UART as standard output. + /// + /// TX to pin 14 + /// RX to pin 15 + pub fn map_pl011_uart(&self) { + let mut r = &self.inner; + r.lock(|inner| { + // Map to pins. + inner + .GPFSEL1 + .modify(GPFSEL1::FSEL14::AltFunc0 + GPFSEL1::FSEL15::AltFunc0); + + // Enable pins 14 and 15. + inner.GPPUD.set(0); + arch::spin_for_cycles(150); + + inner + .GPPUDCLK0 + .write(GPPUDCLK0::PUDCLK14::AssertClock + GPPUDCLK0::PUDCLK15::AssertClock); + arch::spin_for_cycles(150); + + inner.GPPUDCLK0.set(0); + }) + } +} + +//-------------------------------------------------------------------------------------------------- +// OS interface implementations +//-------------------------------------------------------------------------------------------------- + +impl interface::driver::DeviceDriver for GPIO { + fn compatible(&self) -> &str { + "GPIO" + } +} diff --git a/13_integrated_testing/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs b/13_integrated_testing/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs new file mode 100644 index 00000000..5fab617b --- /dev/null +++ b/13_integrated_testing/src/bsp/driver/bcm/bcm2xxx_pl011_uart.rs @@ -0,0 +1,340 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2019 Andre Richter + +//! PL011 UART driver. + +use crate::{arch, arch::sync::NullLock, interface}; +use core::{fmt, ops}; +use register::{mmio::*, register_bitfields, register_fields}; + +// Temporary workaround. +use patches::register_structs; + +// PL011 UART registers. +// +// Descriptions taken from +// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf +register_bitfields! { + u32, + + /// Flag Register + FR [ + /// Transmit FIFO empty. The meaning of this bit depends on the state of the FEN bit in the + /// Line Control Register, UARTLCR_ LCRH. + /// + /// If the FIFO is disabled, this bit is set when the transmit holding register is empty. If + /// the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty. This bit does + /// not indicate if there is data in the transmit shift register. + TXFE OFFSET(7) NUMBITS(1) [], + + /// Transmit FIFO full. The meaning of this bit depends on the state of the FEN bit in the + /// UARTLCR_ LCRH Register. + /// + /// If the FIFO is disabled, this bit is set when the transmit holding register is full. If + /// the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full. + TXFF OFFSET(5) NUMBITS(1) [], + + /// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the + /// UARTLCR_H Register. + /// + /// If the FIFO is disabled, this bit is set when the receive holding register is empty. If + /// the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty. + RXFE OFFSET(4) NUMBITS(1) [] + ], + + /// Integer Baud rate divisor + IBRD [ + /// Integer Baud rate divisor + IBRD OFFSET(0) NUMBITS(16) [] + ], + + /// Fractional Baud rate divisor + FBRD [ + /// Fractional Baud rate divisor + FBRD OFFSET(0) NUMBITS(6) [] + ], + + /// Line Control register + LCRH [ + /// Word length. These bits indicate the number of data bits transmitted or received in a + /// frame. + WLEN OFFSET(5) NUMBITS(2) [ + FiveBit = 0b00, + SixBit = 0b01, + SevenBit = 0b10, + EightBit = 0b11 + ], + + /// Enable FIFOs: + /// + /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become 1-byte-deep holding + /// registers + /// + /// 1 = transmit and receive FIFO buffers are enabled (FIFO mode). + FEN OFFSET(4) NUMBITS(1) [ + FifosDisabled = 0, + FifosEnabled = 1 + ] + ], + + /// Control Register + CR [ + /// Receive enable. If this bit is set to 1, the receive section of the UART is enabled. + /// Data reception occurs for UART signals. When the UART is disabled in the middle of + /// reception, it completes the current character before stopping. + RXE OFFSET(9) NUMBITS(1) [ + Disabled = 0, + Enabled = 1 + ], + + /// Transmit enable. If this bit is set to 1, the transmit section of the UART is enabled. + /// Data transmission occurs for UART signals. When the UART is disabled in the middle of + /// transmission, it completes the current character before stopping. + TXE OFFSET(8) NUMBITS(1) [ + Disabled = 0, + Enabled = 1 + ], + + /// UART enable + UARTEN OFFSET(0) NUMBITS(1) [ + /// If the UART is disabled in the middle of transmission or reception, it completes the + /// current character before stopping. + Disabled = 0, + Enabled = 1 + ] + ], + + /// Interrupt Clear Register + ICR [ + /// Meta field for all pending interrupts + ALL OFFSET(0) NUMBITS(11) [] + ] +} + +register_structs! { + #[allow(non_snake_case)] + RegisterBlock { + (0x00 => DR: ReadWrite), + (0x04 => _reserved1), + (0x18 => FR: ReadOnly), + (0x1c => _reserved2), + (0x24 => IBRD: WriteOnly), + (0x28 => FBRD: WriteOnly), + (0x2c => LCRH: WriteOnly), + (0x30 => CR: WriteOnly), + (0x34 => _reserved3), + (0x44 => ICR: WriteOnly), + (0x48 => @END), + } +} + +/// The driver's mutex protected part. +pub struct PL011UartInner { + base_addr: usize, + chars_written: usize, + chars_read: usize, +} + +/// Deref to RegisterBlock. +/// +/// Allows writing +/// ``` +/// self.DR.read() +/// ``` +/// instead of something along the lines of +/// ``` +/// unsafe { (*PL011UartInner::ptr()).DR.read() } +/// ``` +impl ops::Deref for PL011UartInner { + type Target = RegisterBlock; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.ptr() } + } +} + +impl PL011UartInner { + pub const unsafe fn new(base_addr: usize) -> PL011UartInner { + PL011UartInner { + base_addr, + chars_written: 0, + chars_read: 0, + } + } + + /// Set up baud rate and characteristics. + /// + /// Results in 8N1 and 115200 baud (if the clk has been previously set to 4 MHz by the + /// firmware). + pub fn init(&self) { + // Turn it off temporarily. + self.CR.set(0); + + self.ICR.write(ICR::ALL::CLEAR); + self.IBRD.write(IBRD::IBRD.val(26)); // Results in 115200 baud for UART Clk of 48 MHz. + self.FBRD.write(FBRD::FBRD.val(3)); + self.LCRH + .write(LCRH::WLEN::EightBit + LCRH::FEN::FifosEnabled); // 8N1 + Fifo on + self.CR + .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled); + } + + /// Return a pointer to the register block. + fn ptr(&self) -> *const RegisterBlock { + self.base_addr as *const _ + } + + /// Send a character. + fn write_char(&mut self, c: char) { + // Spin while TX FIFO full is set, waiting for an empty slot. + while self.FR.matches_all(FR::TXFF::SET) { + arch::nop(); + } + + // Write the character to the buffer. + self.DR.set(c as u32); + } +} + +/// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are +/// used to implement the `kernel`'s `print!` and `println!` macros. By implementing `write_str()`, +/// we get `write_fmt()` automatically. +/// +/// The function takes an `&mut self`, so it must be implemented for the inner struct. +/// +/// See [`src/print.rs`]. +/// +/// [`src/print.rs`]: ../../print/index.html +impl fmt::Write for PL011UartInner { + fn write_str(&mut self, s: &str) -> fmt::Result { + for c in s.chars() { + // Convert newline to carrige return + newline. + if c == '\n' { + self.write_char('\r') + } + + self.write_char(c); + } + + self.chars_written += s.len(); + + Ok(()) + } +} + +//-------------------------------------------------------------------------------------------------- +// Export the inner struct so that BSPs can use it for the panic handler +//-------------------------------------------------------------------------------------------------- +pub use PL011UartInner as PanicUart; + +//-------------------------------------------------------------------------------------------------- +// BSP-public +//-------------------------------------------------------------------------------------------------- + +/// The driver's main struct. +pub struct PL011Uart { + inner: NullLock, +} + +impl PL011Uart { + /// # Safety + /// + /// The user must ensure to provide the correct `base_addr`. + pub const unsafe fn new(base_addr: usize) -> PL011Uart { + PL011Uart { + inner: NullLock::new(PL011UartInner::new(base_addr)), + } + } +} + +//-------------------------------------------------------------------------------------------------- +// OS interface implementations +//-------------------------------------------------------------------------------------------------- +use interface::sync::Mutex; + +impl interface::driver::DeviceDriver for PL011Uart { + fn compatible(&self) -> &str { + "PL011Uart" + } + + fn init(&self) -> interface::driver::Result { + let mut r = &self.inner; + r.lock(|inner| inner.init()); + + Ok(()) + } +} + +impl interface::console::Write for PL011Uart { + /// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded by a Mutex to + /// serialize access. + fn write_char(&self, c: char) { + let mut r = &self.inner; + r.lock(|inner| inner.write_char(c)); + } + + fn write_fmt(&self, args: core::fmt::Arguments) -> fmt::Result { + // Fully qualified syntax for the call to `core::fmt::Write::write:fmt()` to increase + // readability. + let mut r = &self.inner; + r.lock(|inner| fmt::Write::write_fmt(inner, args)) + } + + fn flush(&self) { + let mut r = &self.inner; + // Spin until TX FIFO empty is set. + r.lock(|inner| { + while !inner.FR.matches_all(FR::TXFE::SET) { + arch::nop(); + } + }); + } +} + +impl interface::console::Read for PL011Uart { + fn read_char(&self) -> char { + let mut r = &self.inner; + r.lock(|inner| { + // Spin while RX FIFO empty is set. + while inner.FR.matches_all(FR::RXFE::SET) { + arch::nop(); + } + + // Read one character. + let mut ret = inner.DR.get() as u8 as char; + + // Convert carrige return to newline. + if ret == '\r' { + ret = '\n' + } + + // Update statistics. + inner.chars_read += 1; + + ret + }) + } + + fn clear(&self) { + let mut r = &self.inner; + r.lock(|inner| { + // Read from the RX FIFO until it is indicating empty. + while !inner.FR.matches_all(FR::RXFE::SET) { + inner.DR.get(); + } + }) + } +} + +impl interface::console::Statistics for PL011Uart { + fn chars_written(&self) -> usize { + let mut r = &self.inner; + r.lock(|inner| inner.chars_written) + } + + fn chars_read(&self) -> usize { + let mut r = &self.inner; + r.lock(|inner| inner.chars_read) + } +} diff --git a/13_integrated_testing/src/bsp/rpi.rs b/13_integrated_testing/src/bsp/rpi.rs new file mode 100644 index 00000000..131b9d30 --- /dev/null +++ b/13_integrated_testing/src/bsp/rpi.rs @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2019 Andre Richter + +//! Board Support Package for the Raspberry Pi. + +mod memory_map; +mod virt_mem_layout; + +use super::driver; +use crate::{interface, memory::KernelVirtualLayout}; +use core::fmt; + +/// Used by `arch` code to find the early boot core. +pub const BOOT_CORE_ID: u64 = 0; + +/// The early boot core's stack address. +pub const BOOT_CORE_STACK_START: u64 = 0x80_000; + +//-------------------------------------------------------------------------------------------------- +// Global BSP driver instances +//-------------------------------------------------------------------------------------------------- + +static GPIO: driver::GPIO = unsafe { driver::GPIO::new(memory_map::mmio::GPIO_BASE) }; +static PL011_UART: driver::PL011Uart = + unsafe { driver::PL011Uart::new(memory_map::mmio::PL011_UART_BASE) }; + +//-------------------------------------------------------------------------------------------------- +// Implementation of the kernel's BSP calls +//-------------------------------------------------------------------------------------------------- + +/// Board identification. +pub fn board_name() -> &'static str { + #[cfg(feature = "bsp_rpi3")] + { + "Raspberry Pi 3" + } + + #[cfg(feature = "bsp_rpi4")] + { + "Raspberry Pi 4" + } +} + +/// Return a reference to a `console::All` implementation. +pub fn console() -> &'static impl interface::console::All { + &PL011_UART +} + +/// In case of a panic, the panic handler uses this function to take a last shot at printing +/// something before the system is halted. +/// +/// # Safety +/// +/// - Use only for printing during a panic. +pub unsafe fn panic_console_out() -> impl fmt::Write { + let uart = driver::PanicUart::new(memory_map::mmio::PL011_UART_BASE); + uart.init(); + uart +} + +/// Return an array of references to all `DeviceDriver` compatible `BSP` drivers. +/// +/// # Safety +/// +/// The order of devices is the order in which `DeviceDriver::init()` is called. +pub fn device_drivers() -> [&'static dyn interface::driver::DeviceDriver; 2] { + [&GPIO, &PL011_UART] +} + +/// BSP initialization code that runs after driver init. +pub fn post_driver_init() { + // Configure PL011Uart's output pins. + GPIO.map_pl011_uart(); +} + +/// Return the address space size in bytes. +pub const fn addr_space_size() -> usize { + memory_map::END_INCLUSIVE + 1 +} + +/// Return a reference to the virtual memory layout. +pub fn virt_mem_layout() -> &'static KernelVirtualLayout<{ virt_mem_layout::NUM_MEM_RANGES }> { + &virt_mem_layout::LAYOUT +} + +//-------------------------------------------------------------------------------------------------- +// Testing +//-------------------------------------------------------------------------------------------------- + +/// Minimal code needed to bring up the console in QEMU (for testing only). This is often less steps +/// than on real hardware due to QEMU's abstractions. +/// +/// For the RPi, nothing needs to be done. +pub fn qemu_bring_up_console() {} diff --git a/13_integrated_testing/src/bsp/rpi/link.ld b/13_integrated_testing/src/bsp/rpi/link.ld new file mode 100644 index 00000000..5745d96d --- /dev/null +++ b/13_integrated_testing/src/bsp/rpi/link.ld @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: MIT OR Apache-2.0 + * + * Copyright (c) 2018-2019 Andre Richter + */ + +SECTIONS +{ + /* Set current address to the value from which the RPi starts execution */ + . = 0x80000; + + __ro_start = .; + .text : + { + *(.text._start) *(.text*) + } + + .rodata : + { + *(.rodata*) + } + . = ALIGN(65536); /* Fill up to 64 KiB */ + __ro_end = .; + + .data : + { + *(.data*) + } + + /* Align to 8 byte boundary */ + .bss ALIGN(8): + { + __bss_start = .; + *(.bss*); + __bss_end = .; + } + + /DISCARD/ : { *(.comment*) } +} diff --git a/13_integrated_testing/src/bsp/rpi/memory_map.rs b/13_integrated_testing/src/bsp/rpi/memory_map.rs new file mode 100644 index 00000000..42615582 --- /dev/null +++ b/13_integrated_testing/src/bsp/rpi/memory_map.rs @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2019 Andre Richter + +//! The board's memory map. + +#[cfg(feature = "bsp_rpi3")] +#[rustfmt::skip] +pub const END_INCLUSIVE: usize = 0x3FFF_FFFF; + +#[cfg(feature = "bsp_rpi4")] +#[rustfmt::skip] +pub const END_INCLUSIVE: usize = 0xFFFF_FFFF; + +/// Physical devices. +#[rustfmt::skip] +pub mod mmio { + #[cfg(feature = "bsp_rpi3")] + pub const BASE: usize = 0x3F00_0000; + + #[cfg(feature = "bsp_rpi4")] + pub const BASE: usize = 0xFE00_0000; + + pub const GPIO_BASE: usize = BASE + 0x0020_0000; + pub const PL011_UART_BASE: usize = BASE + 0x0020_1000; + pub const END_INCLUSIVE: usize = super::END_INCLUSIVE; +} diff --git a/13_integrated_testing/src/bsp/rpi/virt_mem_layout.rs b/13_integrated_testing/src/bsp/rpi/virt_mem_layout.rs new file mode 100644 index 00000000..5286c852 --- /dev/null +++ b/13_integrated_testing/src/bsp/rpi/virt_mem_layout.rs @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2019 Andre Richter + +//! The virtual memory layout. +//! +//! The layout must contain only special ranges, aka anything that is _not_ normal cacheable DRAM. +//! It is agnostic of the paging granularity that the architecture's MMU will use. + +use super::memory_map; +use crate::memory::*; +use core::ops::RangeInclusive; + +//-------------------------------------------------------------------------------------------------- +// BSP-public +//-------------------------------------------------------------------------------------------------- + +pub const NUM_MEM_RANGES: usize = 2; + +pub static LAYOUT: KernelVirtualLayout<{ NUM_MEM_RANGES }> = KernelVirtualLayout::new( + memory_map::END_INCLUSIVE, + [ + RangeDescriptor { + name: "Kernel code and RO data", + virtual_range: || { + // Using the linker script, we ensure that the RO area is consecutive and 4 KiB + // aligned, and we export the boundaries via symbols: + // + // [__ro_start, __ro_end) + extern "C" { + // The inclusive start of the read-only area, aka the address of the first + // byte of the area. + static __ro_start: u64; + + // The exclusive end of the read-only area, aka the address of the first + // byte _after_ the RO area. + static __ro_end: u64; + } + + unsafe { + // Notice the subtraction to turn the exclusive end into an inclusive end. + #[allow(clippy::range_minus_one)] + RangeInclusive::new( + &__ro_start as *const _ as usize, + &__ro_end as *const _ as usize - 1, + ) + } + }, + translation: Translation::Identity, + attribute_fields: AttributeFields { + mem_attributes: MemAttributes::CacheableDRAM, + acc_perms: AccessPermissions::ReadOnly, + execute_never: false, + }, + }, + RangeDescriptor { + name: "Device MMIO", + virtual_range: || { + RangeInclusive::new(memory_map::mmio::BASE, memory_map::mmio::END_INCLUSIVE) + }, + translation: Translation::Identity, + attribute_fields: AttributeFields { + mem_attributes: MemAttributes::Device, + acc_perms: AccessPermissions::ReadWrite, + execute_never: true, + }, + }, + ], +); diff --git a/13_integrated_testing/src/interface.rs b/13_integrated_testing/src/interface.rs new file mode 100644 index 00000000..19dad63c --- /dev/null +++ b/13_integrated_testing/src/interface.rs @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2019 Andre Richter + +//! Trait definitions for coupling `kernel` and `BSP` code. +//! +//! ``` +//! +-------------------+ +//! | Interface (Trait) | +//! | | +//! +--+-------------+--+ +//! ^ ^ +//! | | +//! | | +//! +----------+--+ +--+----------+ +//! | Kernel code | | BSP Code | +//! | | | | +//! +-------------+ +-------------+ +//! ``` + +/// System console operations. +pub mod console { + use core::fmt; + + /// Console write functions. + pub trait Write { + /// Write a single character. + fn write_char(&self, c: char); + + /// Write a Rust format string. + fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result; + + /// Block execution until the last character has been physically put on the TX wire + /// (draining TX buffers/FIFOs, if any). + fn flush(&self); + } + + /// Console read functions. + pub trait Read { + /// Read a single character. + fn read_char(&self) -> char { + ' ' + } + + /// Clear RX buffers, if any. + fn clear(&self); + } + + /// Console statistics. + pub trait Statistics { + /// Return the number of characters written. + fn chars_written(&self) -> usize { + 0 + } + + /// Return the number of characters read. + fn chars_read(&self) -> usize { + 0 + } + } + + /// Trait alias for a full-fledged console. + pub trait All = Write + Read + Statistics; +} + +/// Synchronization primitives. +pub mod sync { + /// Any object implementing this trait guarantees exclusive access to the data contained within + /// the mutex for the duration of the lock. + /// + /// The trait follows the [Rust embedded WG's + /// proposal](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md) and therefore + /// provides some goodness such as [deadlock + /// prevention](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md#design-decisions-and-compatibility). + /// + /// # Example + /// + /// Since the lock function takes an `&mut self` to enable deadlock-prevention, the trait is + /// best implemented **for a reference to a container struct**, and has a usage pattern that + /// might feel strange at first: + /// + /// ``` + /// static MUT: Mutex> = Mutex::new(RefCell::new(0)); + /// + /// fn foo() { + /// let mut r = &MUT; // Note that r is mutable + /// r.lock(|data| *data += 1); + /// } + /// ``` + pub trait Mutex { + /// Type of data encapsulated by the mutex. + type Data; + + /// Creates a critical section and grants temporary mutable access to the encapsulated data. + fn lock(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R; + } +} + +/// Driver interfaces. +pub mod driver { + /// Driver result type, e.g. for indicating successful driver init. + pub type Result = core::result::Result<(), ()>; + + /// Device Driver functions. + pub trait DeviceDriver { + /// Return a compatibility string for identifying the driver. + fn compatible(&self) -> &str; + + /// Called by the kernel to bring up the device. + fn init(&self) -> Result { + Ok(()) + } + } +} + +/// Timekeeping interfaces. +pub mod time { + use core::time::Duration; + + /// Timer functions. + pub trait Timer { + /// The timer's resolution. + fn resolution(&self) -> Duration; + + /// The uptime since power-on of the device. + /// + /// This includes time consumed by firmware and bootloaders. + fn uptime(&self) -> Duration; + + /// Spin for a given duration. + fn spin_for(&self, duration: Duration); + } +} + +/// Memory Management interfaces. +pub mod mm { + /// MMU functions. + pub trait MMU { + /// Called by the kernel early during init. + unsafe fn init(&self) -> Result<(), &'static str>; + } +} diff --git a/13_integrated_testing/src/lib.rs b/13_integrated_testing/src/lib.rs new file mode 100644 index 00000000..8857dddb --- /dev/null +++ b/13_integrated_testing/src/lib.rs @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2019 Andre Richter + +// Rust embedded logo for `make doc`. +#![doc(html_logo_url = "https://git.io/JeGIp")] + +//! The `kernel` library. +//! +//! Used by `main.rs` to compose the final kernel binary. + +#![allow(incomplete_features)] +#![feature(const_generics)] +#![feature(format_args_nl)] +#![feature(global_asm)] +#![feature(linkage)] +#![feature(panic_info_message)] +#![feature(trait_alias)] +#![no_std] +// Testing +#![cfg_attr(test, no_main)] +#![feature(custom_test_frameworks)] +#![reexport_test_harness_main = "test_main"] +#![test_runner(crate::test_runner)] + +// Conditionally includes the selected `architecture` code, which provides the `_start()` function, +// the first function to run. +pub mod arch; + +// `_start()` then calls `runtime_init()`, which on completion, jumps to `kernel_init()`. +mod runtime_init; + +// Conditionally includes the selected `BSP` code. +pub mod bsp; + +pub mod interface; +mod memory; +mod panic_wait; +pub mod print; + +//-------------------------------------------------------------------------------------------------- +// Testing +//-------------------------------------------------------------------------------------------------- + +/// The default runner for unit tests. +pub fn test_runner(tests: &[&test_types::UnitTest]) { + println!("Running {} tests", tests.len()); + println!("-------------------------------------------------------------------\n"); + for (i, test) in tests.iter().enumerate() { + print!("{:>3}. {:.<58}", i + 1, test.name); + + // Run the actual test. + (test.test_func)(); + + // Failed tests call panic!(). Execution reaches here only if the test has passed. + println!("[ok]") + } +} + +/// The `kernel_init()` for unit tests. Called from `runtime_init()`. +#[cfg(test)] +#[no_mangle] +unsafe fn kernel_init() -> ! { + bsp::qemu_bring_up_console(); + + test_main(); + + arch::qemu_exit_success() +} diff --git a/13_integrated_testing/src/main.rs b/13_integrated_testing/src/main.rs new file mode 100644 index 00000000..31b5b50d --- /dev/null +++ b/13_integrated_testing/src/main.rs @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2019 Andre Richter + +// Rust embedded logo for `make doc`. +#![doc(html_logo_url = "https://git.io/JeGIp")] + +//! The `kernel` binary. +//! +//! The `kernel` is composed by glueing together code from +//! +//! - [Hardware-specific Board Support Packages] (`BSPs`). +//! - [Architecture-specific code]. +//! - HW- and architecture-agnostic `kernel` code. +//! +//! using the [`kernel::interface`] traits. +//! +//! [Hardware-specific Board Support Packages]: bsp/index.html +//! [Architecture-specific code]: arch/index.html +//! [`kernel::interface`]: interface/index.html + +#![feature(format_args_nl)] +#![no_main] +#![no_std] + +use libkernel::{arch, bsp, info, interface}; + +/// Early init code. +/// +/// Concerned with with initializing `BSP` and `arch` parts. +/// +/// # Safety +/// +/// - Only a single core must be active and running this function. +/// - The init calls in this function must appear in the correct order: +/// - Virtual memory must be activated before the device drivers. +/// - Without it, any atomic operations, e.g. the yet-to-be-introduced spinlocks in the device +/// drivers (which currently employ NullLocks instead of spinlocks), will fail to work on +/// the RPi SoCs. +#[no_mangle] +unsafe fn kernel_init() -> ! { + use interface::mm::MMU; + + arch::enable_exception_handling(); + + if let Err(string) = arch::mmu().init() { + panic!("MMU: {}", string); + } + + for i in bsp::device_drivers().iter() { + if let Err(()) = i.init() { + panic!("Error loading driver: {}", i.compatible()) + } + } + bsp::post_driver_init(); + // println! is usable from here on. + + // Transition from unsafe to safe. + kernel_main() +} + +/// The main function running after the early init. +fn kernel_main() -> ! { + use interface::console::All; + + info!("Booting on: {}", bsp::board_name()); + + info!("MMU online. Special regions:"); + bsp::virt_mem_layout().print_layout(); + + let (_, privilege_level) = arch::state::current_privilege_level(); + info!("Current privilege level: {}", privilege_level); + + info!("Exception handling state:"); + arch::state::print_exception_state(); + + info!( + "Architectural timer resolution: {} ns", + arch::timer().resolution().as_nanos() + ); + + info!("Drivers loaded:"); + for (i, driver) in bsp::device_drivers().iter().enumerate() { + info!(" {}. {}", i + 1, driver.compatible()); + } + + // Will never reach here in this tutorial. + info!("Echoing input now"); + loop { + let c = bsp::console().read_char(); + bsp::console().write_char(c); + } +} diff --git a/13_integrated_testing/src/memory.rs b/13_integrated_testing/src/memory.rs new file mode 100644 index 00000000..058516ad --- /dev/null +++ b/13_integrated_testing/src/memory.rs @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2019 Andre Richter + +//! Memory Management. + +use core::{fmt, ops::RangeInclusive}; + +#[derive(Copy, Clone)] +pub enum Translation { + Identity, + Offset(usize), +} + +#[derive(Copy, Clone)] +pub enum MemAttributes { + CacheableDRAM, + Device, +} + +#[derive(Copy, Clone)] +pub enum AccessPermissions { + ReadOnly, + ReadWrite, +} + +#[derive(Copy, Clone)] +pub struct AttributeFields { + pub mem_attributes: MemAttributes, + pub acc_perms: AccessPermissions, + pub execute_never: bool, +} + +impl Default for AttributeFields { + fn default() -> AttributeFields { + AttributeFields { + mem_attributes: MemAttributes::CacheableDRAM, + acc_perms: AccessPermissions::ReadWrite, + execute_never: true, + } + } +} + +/// An architecture agnostic descriptor for a memory range. +pub struct RangeDescriptor { + pub name: &'static str, + pub virtual_range: fn() -> RangeInclusive, + pub translation: Translation, + pub attribute_fields: AttributeFields, +} + +/// Human-readable output of a RangeDescriptor. +impl fmt::Display for RangeDescriptor { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // Call the function to which self.range points, and dereference the result, which causes + // Rust to copy the value. + let start = *(self.virtual_range)().start(); + let end = *(self.virtual_range)().end(); + let size = end - start + 1; + + // log2(1024). + const KIB_RSHIFT: u32 = 10; + + // log2(1024 * 1024). + const MIB_RSHIFT: u32 = 20; + + let (size, unit) = if (size >> MIB_RSHIFT) > 0 { + (size >> MIB_RSHIFT, "MiB") + } else if (size >> KIB_RSHIFT) > 0 { + (size >> KIB_RSHIFT, "KiB") + } else { + (size, "Byte") + }; + + let attr = match self.attribute_fields.mem_attributes { + MemAttributes::CacheableDRAM => "C", + MemAttributes::Device => "Dev", + }; + + let acc_p = match self.attribute_fields.acc_perms { + AccessPermissions::ReadOnly => "RO", + AccessPermissions::ReadWrite => "RW", + }; + + let xn = if self.attribute_fields.execute_never { + "PXN" + } else { + "PX" + }; + + write!( + f, + " {:#010x} - {:#010x} | {: >3} {} | {: <3} {} {: <3} | {}", + start, end, size, unit, attr, acc_p, xn, self.name + ) + } +} + +/// Type for expressing the kernel's virtual memory layout. +pub struct KernelVirtualLayout { + max_virt_addr_inclusive: usize, + inner: [RangeDescriptor; NUM_SPECIAL_RANGES], +} + +impl KernelVirtualLayout<{ NUM_SPECIAL_RANGES }> { + pub const fn new(max: usize, layout: [RangeDescriptor; NUM_SPECIAL_RANGES]) -> Self { + Self { + max_virt_addr_inclusive: max, + inner: layout, + } + } + + /// For a virtual address, find and return the output address and corresponding attributes. + /// + /// If the address is not found in `inner`, return an identity mapped default with normal + /// cacheable DRAM attributes. + pub fn get_virt_addr_properties( + &self, + virt_addr: usize, + ) -> Result<(usize, AttributeFields), &'static str> { + if virt_addr > self.max_virt_addr_inclusive { + return Err("Address out of range"); + } + + for i in self.inner.iter() { + if (i.virtual_range)().contains(&virt_addr) { + let output_addr = match i.translation { + Translation::Identity => virt_addr, + Translation::Offset(a) => a + (virt_addr - (i.virtual_range)().start()), + }; + + return Ok((output_addr, i.attribute_fields)); + } + } + + Ok((virt_addr, AttributeFields::default())) + } + + /// Print the memory layout. + pub fn print_layout(&self) { + use crate::info; + + for i in self.inner.iter() { + info!("{}", i); + } + } + + #[cfg(test)] + pub fn inner(&self) -> &[RangeDescriptor; NUM_SPECIAL_RANGES] { + &self.inner + } +} diff --git a/13_integrated_testing/src/panic_wait.rs b/13_integrated_testing/src/panic_wait.rs new file mode 100644 index 00000000..f6f7f7ce --- /dev/null +++ b/13_integrated_testing/src/panic_wait.rs @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2019 Andre Richter + +//! A panic handler that infinitely waits. + +use crate::{arch, bsp}; +use core::{fmt, panic::PanicInfo}; + +fn _panic_print(args: fmt::Arguments) { + use fmt::Write; + + unsafe { bsp::panic_console_out().write_fmt(args).unwrap() }; +} + +/// Prints with a newline - only use from the panic handler. +/// +/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html +#[macro_export] +macro_rules! panic_println { + ($($arg:tt)*) => ({ + _panic_print(format_args_nl!($($arg)*)); + }) +} + +/// The point of exit for the "standard" (non-testing) `libkernel`. +/// +/// This code will be used by the release kernel binary and the `integration tests`. It is linked +/// weakly, so that the integration tests can overload it to exit `QEMU` instead of spinning +/// forever. +/// +/// This is one possible approach to solve the problem that `cargo` can not know who the consumer of +/// the library will be: +/// - The release kernel binary that should safely park the paniced core, +/// - or an `integration test` that is executed in QEMU, which should just exit QEMU. +#[cfg(not(test))] +#[linkage = "weak"] +#[no_mangle] +fn _panic_exit() -> ! { + arch::wait_forever() +} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + if let Some(args) = info.message() { + panic_println!("Kernel panic: {}", args); + } else { + panic_println!("Kernel panic!"); + } + + _panic_exit() +} + +//-------------------------------------------------------------------------------------------------- +// Testing +//-------------------------------------------------------------------------------------------------- + +/// The point of exit when the library is compiled for testing. +#[cfg(test)] +#[no_mangle] +fn _panic_exit() -> ! { + arch::qemu_exit_failure() +} diff --git a/13_integrated_testing/src/print.rs b/13_integrated_testing/src/print.rs new file mode 100644 index 00000000..16f1fb21 --- /dev/null +++ b/13_integrated_testing/src/print.rs @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2019 Andre Richter + +//! Printing facilities. + +use crate::{bsp, interface}; +use core::fmt; + +#[doc(hidden)] +pub fn _print(args: fmt::Arguments) { + use interface::console::Write; + + bsp::console().write_fmt(args).unwrap(); +} + +/// Prints without a newline. +/// +/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::print::_print(format_args!($($arg)*))); +} + +/// Prints with a newline. +/// +/// Carbon copy from https://doc.rust-lang.org/src/std/macros.rs.html +#[macro_export] +macro_rules! println { + () => ($crate::print!("\n")); + ($($arg:tt)*) => ({ + $crate::print::_print(format_args_nl!($($arg)*)); + }) +} + +/// Prints an info, with newline. +#[macro_export] +macro_rules! info { + ($string:expr) => ({ + #[allow(unused_imports)] + use crate::interface::time::Timer; + + let timestamp = $crate::arch::timer().uptime(); + let timestamp_subsec_us = timestamp.subsec_micros(); + + $crate::print::_print(format_args_nl!( + concat!("[ {:>3}.{:03}{:03}] ", $string), + timestamp.as_secs(), + timestamp_subsec_us / 1_000, + timestamp_subsec_us % 1_000 + )); + }); + ($format_string:expr, $($arg:tt)*) => ({ + #[allow(unused_imports)] + use crate::interface::time::Timer; + + let timestamp = $crate::arch::timer().uptime(); + let timestamp_subsec_us = timestamp.subsec_micros(); + + $crate::print::_print(format_args_nl!( + concat!("[ {:>3}.{:03}{:03}] ", $format_string), + timestamp.as_secs(), + timestamp_subsec_us / 1_000, + timestamp_subsec_us % 1_000, + $($arg)* + )); + }) +} + +/// Prints a warning, with newline. +#[macro_export] +macro_rules! warn { + ($string:expr) => ({ + #[allow(unused_imports)] + use crate::interface::time::Timer; + + let timestamp = $crate::arch::timer().uptime(); + let timestamp_subsec_us = timestamp.subsec_micros(); + + $crate::print::_print(format_args_nl!( + concat!("[W {:>3}.{:03}{:03}] ", $string), + timestamp.as_secs(), + timestamp_subsec_us / 1_000, + timestamp_subsec_us % 1_000 + )); + }); + ($format_string:expr, $($arg:tt)*) => ({ + #[allow(unused_imports)] + use crate::interface::time::Timer; + + let timestamp = $crate::arch::timer().uptime(); + let timestamp_subsec_us = timestamp.subsec_micros(); + + $crate::print::_print(format_args_nl!( + concat!("[W {:>3}.{:03}{:03}] ", $format_string), + timestamp.as_secs(), + timestamp_subsec_us / 1_000, + timestamp_subsec_us % 1_000, + $($arg)* + )); + }) +} diff --git a/13_integrated_testing/src/runtime_init.rs b/13_integrated_testing/src/runtime_init.rs new file mode 100644 index 00000000..56e9a650 --- /dev/null +++ b/13_integrated_testing/src/runtime_init.rs @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2019 Andre Richter + +//! Rust runtime initialization code. + +/// Equivalent to `crt0` or `c0` code in C/C++ world. Clears the `bss` section, then jumps to kernel +/// init code. +/// +/// # Safety +/// +/// - Only a single core must be active and running this function. +pub unsafe fn runtime_init() -> ! { + extern "C" { + // Boundaries of the .bss section, provided by the linker script. + static mut __bss_start: u64; + static mut __bss_end: u64; + + } + + extern "Rust" { + fn kernel_init() -> !; + } + + // Zero out the .bss section. + r0::zero_bss(&mut __bss_start, &mut __bss_end); + + kernel_init() +} diff --git a/13_integrated_testing/test-macros/Cargo.toml b/13_integrated_testing/test-macros/Cargo.toml new file mode 100644 index 00000000..a570f72b --- /dev/null +++ b/13_integrated_testing/test-macros/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "test-macros" +version = "0.1.0" +authors = ["Andre Richter "] +edition = "2018" + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.x" +quote = "1.x" +syn = { version = "1.x", features = ["full"] } +test-types = { path = "../test-types" } diff --git a/13_integrated_testing/test-macros/src/lib.rs b/13_integrated_testing/test-macros/src/lib.rs new file mode 100644 index 00000000..f730309d --- /dev/null +++ b/13_integrated_testing/test-macros/src/lib.rs @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2019 Andre Richter + +extern crate proc_macro; + +use proc_macro::TokenStream; +use proc_macro2::Span; +use quote::quote; +use syn::{parse_macro_input, Ident, ItemFn}; + +#[proc_macro_attribute] +pub fn kernel_test(_attr: TokenStream, input: TokenStream) -> TokenStream { + let f = parse_macro_input!(input as ItemFn); + + let test_name = &format!("{}", f.sig.ident.to_string()); + let test_ident = Ident::new( + &format!("{}_TEST_CONTAINER", f.sig.ident.to_string().to_uppercase()), + Span::call_site(), + ); + let test_code_block = f.block; + + quote!( + #[test_case] + const #test_ident: test_types::UnitTest = test_types::UnitTest { + name: #test_name, + test_func: || #test_code_block, + }; + ) + .into() +} diff --git a/13_integrated_testing/test-types/Cargo.toml b/13_integrated_testing/test-types/Cargo.toml new file mode 100644 index 00000000..a0be2c57 --- /dev/null +++ b/13_integrated_testing/test-types/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "test-types" +version = "0.1.0" +authors = ["Andre Richter "] +edition = "2018" diff --git a/13_integrated_testing/test-types/src/lib.rs b/13_integrated_testing/test-types/src/lib.rs new file mode 100644 index 00000000..91739735 --- /dev/null +++ b/13_integrated_testing/test-types/src/lib.rs @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2019 Andre Richter + +//! Types for the `custom_test_frameworks` implementation. + +#![no_std] + +/// Unit test container. +pub struct UnitTest { + /// Name of the test. + pub name: &'static str, + + /// Function pointer to the test. + pub test_func: fn(), +} diff --git a/13_integrated_testing/tests/00_interface_sanity_console.rb b/13_integrated_testing/tests/00_interface_sanity_console.rb new file mode 100644 index 00000000..f20f40a0 --- /dev/null +++ b/13_integrated_testing/tests/00_interface_sanity_console.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +# SPDX-License-Identifier: MIT OR Apache-2.0 +# +# Copyright (c) 2019 Andre Richter + +require 'expect' + +TIMEOUT_SECS = 3 + +# Verify sending and receiving works as expected. +class TxRxHandshake + def name + 'Transmit and Receive handshake' + end + + def run(qemu_out, qemu_in) + qemu_in.write_nonblock('ABC') + raise('TX/RX test failed') if qemu_out.expect('OK1234', TIMEOUT_SECS).nil? + end +end + +# Check for correct TX statistics implementation. Depends on test 1 being run first. +class TxStatistics + def name + 'Transmit statistics' + end + + def run(qemu_out, _qemu_in) + raise('chars_written reported wrong') if qemu_out.expect('6', TIMEOUT_SECS).nil? + end +end + +# Check for correct RX statistics implementation. Depends on test 1 being run first. +class RxStatistics + def name + 'Receive statistics' + end + + def run(qemu_out, _qemu_in) + raise('chars_read reported wrong') if qemu_out.expect('3', TIMEOUT_SECS).nil? + end +end + +##-------------------------------------------------------------------------------------------------- +## Test registration +##-------------------------------------------------------------------------------------------------- +def subtest_collection + [TxRxHandshake.new, TxStatistics.new, RxStatistics.new] +end diff --git a/13_integrated_testing/tests/00_interface_sanity_console.rs b/13_integrated_testing/tests/00_interface_sanity_console.rs new file mode 100644 index 00000000..b4d5e25d --- /dev/null +++ b/13_integrated_testing/tests/00_interface_sanity_console.rs @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2019 Andre Richter + +//! Console sanity tests - RX, TX and statistics. + +#![feature(format_args_nl)] +#![no_main] +#![no_std] + +mod panic_exit_failure; + +use libkernel::{bsp, interface::console::*, print}; + +#[no_mangle] +unsafe fn kernel_init() -> ! { + bsp::qemu_bring_up_console(); + + // Handshake + assert_eq!(bsp::console().read_char(), 'A'); + assert_eq!(bsp::console().read_char(), 'B'); + assert_eq!(bsp::console().read_char(), 'C'); + print!("OK1234"); + + // 6 + print!("{}", bsp::console().chars_written()); + + // 3 + print!("{}", bsp::console().chars_read()); + + // The QEMU process running this test will be closed by the I/O test harness. + loop {} +} diff --git a/13_integrated_testing/tests/01_interface_sanity_timer.rs b/13_integrated_testing/tests/01_interface_sanity_timer.rs new file mode 100644 index 00000000..982c8387 --- /dev/null +++ b/13_integrated_testing/tests/01_interface_sanity_timer.rs @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2019 Andre Richter + +//! Timer sanity tests. + +#![feature(custom_test_frameworks)] +#![no_main] +#![no_std] +#![reexport_test_harness_main = "test_main"] +#![test_runner(libkernel::test_runner)] + +mod panic_exit_failure; + +use core::time::Duration; +use libkernel::{arch, arch::timer, bsp, interface::time::Timer}; +use test_macros::kernel_test; + +#[no_mangle] +unsafe fn kernel_init() -> ! { + bsp::qemu_bring_up_console(); + + // Depending on CPU arch, some timer bring-up code could go here. Not needed for the RPi. + + test_main(); + + arch::qemu_exit_success() +} + +/// Simple check that the timer is running. +#[kernel_test] +fn timer_is_counting() { + assert!(timer().uptime().as_nanos() > 0) +} + +/// Timer resolution must be sufficient. +#[kernel_test] +fn timer_resolution_is_sufficient() { + assert!(timer().resolution().as_nanos() < 100) +} + +/// Sanity check spin_for() implementation. +#[kernel_test] +fn spin_accuracy_check_1_second() { + let t1 = timer().uptime(); + timer().spin_for(Duration::from_secs(1)); + let t2 = timer().uptime(); + + assert_eq!((t2 - t1).as_secs(), 1) +} diff --git a/13_integrated_testing/tests/02_arch_exception_handling.rs b/13_integrated_testing/tests/02_arch_exception_handling.rs new file mode 100644 index 00000000..9d90d0ea --- /dev/null +++ b/13_integrated_testing/tests/02_arch_exception_handling.rs @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2019 Andre Richter + +//! Page faults must result in synchronous exceptions. + +#![feature(format_args_nl)] +#![no_main] +#![no_std] + +/// Overwrites libkernel's `panic_wait::_panic_exit()` with the QEMU-exit version. +/// +/// Reaching this code is a success, because it is called from the synchronous exception handler, +/// which is what this test wants to achieve. +/// +/// It also means that this integration test can not use any other code that calls panic!() directly +/// or indirectly. +mod panic_exit_success; + +use libkernel::{arch, bsp, interface::mm::MMU, println}; + +#[no_mangle] +unsafe fn kernel_init() -> ! { + bsp::qemu_bring_up_console(); + + println!("Testing synchronous exception handling by causing a page fault"); + println!("-------------------------------------------------------------------\n"); + + arch::enable_exception_handling(); + + if let Err(string) = arch::mmu().init() { + println!("MMU: {}", string); + arch::qemu_exit_failure() + } + + println!("Writing beyond mapped area to address 9 GiB..."); + let big_addr: u64 = 9 * 1024 * 1024 * 1024; + core::ptr::read_volatile(big_addr as *mut u64); + + // If execution reaches here, the memory access above did not cause a page fault exception. + arch::qemu_exit_failure() +} diff --git a/13_integrated_testing/tests/panic_exit_failure/mod.rs b/13_integrated_testing/tests/panic_exit_failure/mod.rs new file mode 100644 index 00000000..12d31b37 --- /dev/null +++ b/13_integrated_testing/tests/panic_exit_failure/mod.rs @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2019 Andre Richter + +/// Overwrites libkernel's `panic_wait::_panic_exit()` with the QEMU-exit version. +#[no_mangle] +fn _panic_exit() -> ! { + libkernel::arch::qemu_exit_failure() +} diff --git a/13_integrated_testing/tests/panic_exit_success/mod.rs b/13_integrated_testing/tests/panic_exit_success/mod.rs new file mode 100644 index 00000000..58a7fba7 --- /dev/null +++ b/13_integrated_testing/tests/panic_exit_success/mod.rs @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2019 Andre Richter + +/// Overwrites libkernel's `panic_wait::_panic_exit()` with the QEMU-exit version. +#[no_mangle] +fn _panic_exit() -> ! { + libkernel::arch::qemu_exit_success() +} diff --git a/13_integrated_testing/tests/runner.rb b/13_integrated_testing/tests/runner.rb new file mode 100755 index 00000000..df22bc75 --- /dev/null +++ b/13_integrated_testing/tests/runner.rb @@ -0,0 +1,137 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# SPDX-License-Identifier: MIT OR Apache-2.0 +# +# Copyright (c) 2019 Andre Richter + +require 'pty' + +# Test base class. +class Test + INDENT = ' ' + + def print_border(status) + puts + puts "#{INDENT}-------------------------------------------------------------------" + puts status + puts "#{INDENT}-------------------------------------------------------------------\n\n\n" + end + + def print_error(error) + puts + print_border("#{INDENT}❌ Failure: #{error}: #{@test_name}") + end + + def print_success + print_border("#{INDENT}✔️ Success: #{@test_name}") + end + + def print_output + puts "#{INDENT}-------------------------------------------------------------------" + print INDENT + print '🦀 ' + print @output.join('').gsub("\n", "\n" + INDENT) + end + + def finish(error) + print_output + + exit_code = if error + print_error(error) + false + else + print_success + true + end + + exit(exit_code) + end +end + +# Executes tests with console I/O. +class ConsoleTest < Test + def initialize(binary, qemu_cmd, test_name, console_subtests) + @binary = binary + @qemu_cmd = qemu_cmd + @test_name = test_name + @console_subtests = console_subtests + @cur_subtest = 1 + @output = ["Running #{@console_subtests.length} console-based tests\n", + "-------------------------------------------------------------------\n\n"] + end + + def format_test_name(number, name) + formatted_name = number.to_s.rjust(3) + '. ' + name + formatted_name.ljust(63, '.') + end + + def run_subtest(subtest, qemu_out, qemu_in) + @output << format_test_name(@cur_subtest, subtest.name) + + subtest.run(qemu_out, qemu_in) + + @output << "[ok]\n" + @cur_subtest += 1 + end + + def exec + error = false + + PTY.spawn(@qemu_cmd) do |qemu_out, qemu_in| + begin + @console_subtests.each { |t| run_subtest(t, qemu_out, qemu_in) } + rescue StandardError => e + error = e.message + end + + finish(error) + end + end +end + +# A wrapper around the bare QEMU invocation. +class RawTest < Test + MAX_WAIT_SECS = 5 + + def initialize(binary, qemu_cmd, test_name) + @binary = binary + @qemu_cmd = qemu_cmd + @test_name = test_name + @output = [] + end + + def exec + error = 'Timed out waiting for test' + io = IO.popen(@qemu_cmd) + + while IO.select([io], nil, nil, MAX_WAIT_SECS) + begin + @output << io.read_nonblock(1024) + rescue EOFError + error = false + break + end + end + + finish(error) + end +end + +##-------------------------------------------------------------------------------------------------- +## Script entry point +##-------------------------------------------------------------------------------------------------- +binary = ARGV.last +test_name = binary.gsub(%r{.*deps/}, '').split('-')[0] +console_test_file = 'tests/' + test_name + '.rb' +qemu_cmd = ARGV.join(' ') + +test_runner = if File.exist?(console_test_file) + load console_test_file + # subtest_collection is provided by console_test_file + ConsoleTest.new(binary, qemu_cmd, test_name, subtest_collection) + else + RawTest.new(binary, qemu_cmd, test_name) + end + +test_runner.exec diff --git a/doc/testing_demo.gif b/doc/testing_demo.gif new file mode 100644 index 0000000000000000000000000000000000000000..7e4ab02543350a249f5fb0a0655eeedfc53cc2ac GIT binary patch literal 167337 zcmdqIS6Gwl)-L>}ClHcQqy`KfR5TzZfF*<$dMF}@VkjaaYEVSLme4z)N>f8o5K)7o z0-}VX6afPh6%aKDDq@R@^s?ieYtJ?J!Tk4J>)S{FL9QIUS8~0h-Q&K;cph6j8@xK7SIP)0j&6@H4;W^G$rIvYKm|jMffH~xPjstgc3?l31_af4Wta!QSS}kp`vNm|Fwm4izTuK*Ms|z9MN*L(kw&+Spt^+Bp zgJ9OdAnTF(>%}zJ%M;hDI&1)BHz+G_STC=qLeW##)-(0r2n9Am5gVcSjndeS8rmDz zTWR15&50U%`ng&4r3HUkJlK+F)OX{fT+P)o~D+sp_+8j0hK$PvZ> z(%8V#1SU>`X^>QGNNW0~GRCHQW@dpHa|6?@Vz_PV+_$6gWV`{{bUS%lFhyC8vYtfQ zMzvfcXC7p&W(yUqOP~3lGajbOgnOrT>{pA!+QIz?hb3UoLoYkwKbg$%$-fQ zx&RU`x>hu&9j?AHZtFI?84%sq>$q&$`Z%OL@W4hEk(Y~g&^%jd}U z?MLkP9&tZ(U7eVekQ8mtot5j zNG9{TBkRaXRx~HZ_jqhxQ(SIueB_w~Pv3;SK1tDW$)}jf$b#K^Ri=F8wOjO-#ogL)P3bLzq+XZ`jvq@cL$mV z2Rem={2x!dMxV8{J>!o&e=$2$=lA;2)7ShLf~PBQp1gna+p{;%-@kdWH2y*`E|?l0 zUwGTnHr0FP(ab$wRL1^i%)Y>@*wC#pSv$O-MjPoMg6fEZn>CDDHbP#&c0?Tcu{<9g{Rj7R-a>7C)-f{l{-5afL$^`sCj?ErTM|~ z;@m!$u)FI!pqjO~@2I_Ro%n!w9LmHpc*SD6}@T!7uSp|s!E`e)+OhUDPMurWOv$My(WKwb%1*?Nk{jR zFJD;AG|&KIP@B42LZs~F-^b%G;_j#vsT9PD;eI52zkGi3^3^YmpWGg@pa-UzEkT)f z?=|2uTfW)QQyb|oI|k}tuNyW^oad2x^inHLaa%4P zV_37L9TF9O71Z+s^Cm-~Qt%@dL#q4u^Eo|H?~bhul`Z3{PMyU=q?x;I!3b9zu^c8R ze57qjCdb^zg|EI-O}f|__U&Qm2tg07KH=A`t&)-QQhn?8V9oUQ)1Kr(s|(ibp6Xm1 za1CLgsz&27t<^UEfWu`L84+na0h3^=sdf6-^iV*N)a64xhV`)yN{ccvmd^v^jx2ra zOgzn#rX(@Hg2aX#m>@U~$9%z9TL;%Mz+a*WB2HYrO9$iVCOQW@ISxx)uw?rQkK0aFY@M{6phw0;#7Ib0h$9c1s)VM>Irv z6&#zWMktkz1=2L2vL;I17P=KEidavw%RY%GvuifJXg5 zIw}?kv~+tQ6S-S;l343fQ;JIy^4hn>Cx69XCx|7LZNqHOXaiZ#u!f!C|;l zY+f5Y7qO+?InJL!z7jrh?GBY7K2CthO_d@fLwO12j+fT$?CLIU@>8j2fM6tkvRC^X z={k9+6wxomE4U14txpCenD&U%c#znncwHd}>Pe)4k8mE!kpY%=6{Rqh07%LAJ2(O@ z0Gb1mc%uzN64hkW#(2%LwIuiOGBI2MQ#Pba%$!OP%beV&olRTqJy-;m)+A`@qxh{+ zkIF90_Uzw4WW}kJxEDqe9Um+e_L9&4CKD*+=~+$3dnL6*8`3Sr4sbvp8FXA?D( z-2?fJ9-6J;7D%Bc09u;Rh7ox#MQZt5hE%cSUP~02XI!#t6Y#i@V1(uT2H*%Fhhbfi z2M1+7q)L>f@?0+bR$Ckd2R}wJolq&~ik%a#xJgX8{NN@bnjC7J^T^(JLvkTY?fXY9 zuDVN;SrXKr0D=jCpbP-4!;*~fvfQAf3P9B=v^S;K1Zq1Br%qQ`Anj81jzsZws4};H zL?Z+B8STC+-*Yo>Z$O1K499{kwn>5x^$3r%T~?nB$YfTl+o?z!&MbCTQ8Q+^cGPKRctYLfh$sz0blxL0{cvlim-`Cl`L>U~X60I!(D1 zFAP>W->%--JLP$A;VC!ccFlgLkGozhJge`!ef>o5N8iPT=RC|EZlcpCf2GBtPUkx} z&-H!^GG83#XWXf)bow0Pvp6!;cjwOC-p_|mF1`?8?$&oWeK}gZI6CQkxAA%Jmxz0d zFNGO*o8CKpjefECYOe3@gW2A%XBHP<1K4^V!dV!jv?M^f)VIp@2@}kh#&DVS?b^=Y zQhb))$UmunWYYI7-;^tcxggs)uo|(XW#eydrNPLnGHSroqrU)Sb9f# z($IIJ@5hD3rT1iPBR|o3x=d+#lIqepaISCqvib4{T4v*5rSs2fpXDj9CymeU_Witm za`_`2dvB=2dFE#E@~2>zdn3>LX71cu{>;d{H~QXrw(-UCm&hmgUd{H+K3H7-%EUGa z5H54AN-M%7m!>yzPv#z(uY6-?Hce=|%y;{&d@p#?^v>kTeBa5H9~|ucNn4kNf#Q|v zDwq3HJD)5(ySMU_n|c4!ewW3O7b`RMPwsy?@nrGU;>s)!`#_lJvh+r2b*|Ip!S{1d zmfo4K&hs-LOjo)rPx-7a3_W=;bN9*emy@fD0&Mf@T!+ib_u|#1Ntfn@=TBB4f`>Y3;vCT5O_lV?ehSTbHL z*+`ar0ZXx-g&$(6%&`daF>0h34X+rj$QYf1n056r8-`*w&czVrV+}~LhF-D8k+GzL zShM=rEkm)}=3>e6aTHRVl~{j^hyYdOb9MW2&qp99ZEPnm%xxuJW5Io_ezY2OpGi@jIK{SHI#T} zE|Dpp6hlgi^GZsHOiC(9N~upu8%oMpol9cNCuftAbG?%DBa;gXl8fq-ONNp!%q4T= zQ_4sw6<#TqBU7phQmX4yYKBs-&!uqXQ*V+|>%3C$M5fjkq&C*4HVvgdm`ml!r?ryO z+P%^qMW%HYq;=P)^$ex;&86|>(+5cDgI?*+BGZQo(nspkM~Bj1&7}+EGv1IgCcHA< zMP^JEWK7j(d>YF5GM6Eg&-_lxoc79`iOifU$Xuw;TpG$;nacze*kDsO)SC^DVj~OL zVhwEcFdH+^#wlb;nP$m&XURro$rolRHe}(4vsC7@2nyM1rr8?a*;-NAI)&Nm8nQPG zXK$R(CMx6@nC2LI=NL!jkX{w$m^I{V8P3@@pF>v2rI_YgdFR?hj@`DTWLmKi!hw~55=Q9+} z9W^}{?tLyI>Re>ux#)&-r-sj+nLo!=D2OpFi1RK;h$=`bEJ$f6NE(n78W%WmJAnOm@nig6qT74Rd^R&jw-4uEUIoOsu?c2K3~LDD86Z0T<2YU zC#txBDi=ODje0c%iZAcAUw)7$f;lx2oL})hzrInb_%L!Ncf+=mE`YlbxkDxd1Nku z2}Md$s6;Nu@+?k$A(qcqwMbqjXw}ne>FsF>CjPXWA$fH0 zdYZ6#As~s>mMU?>_bKn2>s#jxsIEseEJmk2Pj9c(1Xrd#txRz}_?ltcm3}r=pIQ}d zrR{Ncqxj*^DaZF)qoVKN8$_L;vm%(&6#30{|44-rI3W1(t#Lsk9pquy2?8fR%3R~1 zLh{y6>;i%QZF8N-uTsBT&?pL&$N~Wmij=0{NL+%2$ia3ck$!%bi8!s9oFQzCbt2Yu z;p5%@a-Mp0#g*MV4ybfxNjZTDH{U*8Z%CR=*3dm><77zk4IG^txY(nkgb)e<>a1 z2WQG{VuqI<((b>Z)eb2fG3J;RU{o9;-f11+< zfatiSQo03zlG2J}1As*=eA%vK5*uTggwvhLDG;<;rb(L|{xpe`D6$`@*g=s#TD~1B zWAADIL(Hj{Ev*C|B9A?_*Ons*@KJscI4aNgkVPBF(jl3sOc~}>3{)4XX=a2cLBx{O zwJzNssC>G%|GZLA59y}rm2GE&#%VyxleM2Cd_Fu@!&CgCK*at=rLE6kg$oQOc+o&dyWsxu@hi7?-Voz())p zBil2%v&b!+U97v8t|vT?z;o# zo267+j4L*u9*!ecf4H%?In>nVf#C-%2!=wdRMB@L62wcctC5&=t4^am+q1o5TF4g3 z5ljQ*fzohw_p{P#oG1Ct=@G%wY{YKxN`7mgxhYFiqt^faghvcNB1%qCqnzF3L} z#eNzT@UHG`Z$$yYQ4Znir9AjYoCC-GhD!6P#RTj8(cBnb^Kp~4gqi5QB!Hw6OgZ61 zOHk0JsXyYY+snT9iLO7d^4uV(mlQE;|GljPg-?MHTU%!l?dY~7-Kt9Fx3_`0cWo!& z+>>7uBtGerImA}QO2sWY=e$&YPUbw>UfXM2=ze!kqGd$8ygC%Ml}(P(_SVU=HIEeH z=plMbm%unX4OYCwE&UxY_kJ7Hhak~p%iQh5WJ$-ZtL?F=7&$t>WlLfle*l(qA9isc zUZj$u&>Nn4fj7p{$RO-f0fmB=!Nsuk8um-Y0@F_i3e;-{FZryy9*3jscaCt|7gBBL zmVMfSri}=DcVL7ZnV?G}|C655FN*gcSll8ieR(R49utdH1#nabA@1yontc<8TWXoS ztTq!%>sN!OuPp4FK=J^o{R;z6!&)|#JDkpB9`wx@_sOGAt1N94Y1+G+e8nV<;|4Z5eBpO$Cd zxrV-c+~bvEJkx?A8?x}}XFqVff`XC~lc{2lwLdG<(DR=|o;%o)kbB%FKMe#xG5Bqp z@Olit_x*S(;-7TN=YJx?;6Ft=BnOU>?wTun@tfL&%~_-lsoi3Ox3t@ARb0ssj(16C zx?=aF>UCQ4>1C^hk3&74YondIb*nA+PTi7#q0cd4DF7-f zyjmV;>nnuQ)6CZsl4KPZ4YYs_HW{Xm)ly$(R)n21I|TxxZl8UJc}lLS3xELxc_KAQ zy}d8}jK1uI(ED3Qn$-G}O-Ygf0)zAIh@dX|AHrb5%+bhyL;8PS`Tu|U#j8I&Y%^Y?$1>?X7%o$T+ zV3GyhFChl;kFxb|L-I~!;~%_k{T~d9L;gRf!bzFEXdJ2|xW0fvw*?3mVrUfMf5h4T zKVRh!0IB`eFm!N~m{^2_)b7a}r6Mv1H=xr>TN5O(i$;zN3j!o~G<0`(m8-gKCSfmw zH^ja8tD=y($1&8^y1x~}JOG9?EMZ--EGH{Moz`{C!*Bgt==_(`G*|9Od* zCJiYW1BylOOwBe#J$vh$ue&C@ohPEGt$0AkH_h+coAy))_8#T_LKimxNiYMKvw;aOqE}?c8y?xwa2W>uQ69DF^0z+J+qG32ND^5Ls z>g@H5-AbEX#X&70uci(9Y~kRJQ=d;ZJlpF?J#rvLqt4UOYbczSTxAXr`O% z7kmE5POIJZY>xhmj;tN`#dG#KK#n{awJQ8U3G6ROq)JSOJf3?@jeue7TuLm~J^(0vBj zmIXfEpR&XB=A}m|vOZH#pnw2~_$I;AvHlymBhHkB`AZ8yir)RtbogDSS+I~GGL^?3 zqr|}_v}7i4lv$s}5&Y>q0th6x(KwVES-bZkR1PXN4P3kMAe3U2T@yT@VV{RLi#m2p zQzAKQ^YYKlzZhViO!&jbmT^jVvTX8#1A&3fKDz?_VjkYsx8>|N=Y8_)>OMIsN!d)6 z{RG*hE3R|QCB{9G@V;rSO9wbR<<`rYAQg4Uoc8->mS3IYJN*u&^YEV{7v+xZJ%4&p z<~4q(ATLV^&E{_ohl-mUC4W-&n9*%BAp^ss|QN#JQf^{#hlgj4bjP@7&ij z^wYC=DfmBO9cx6Ml>WW2?usz7T_kt`G)ih+9DzZ%h{3JQ7gN81Ek*2__Q|AnZCom1 ztv`Pd<1ty8Y?6_$HWd(WDSS}fySHy0NS zS|(^94h1;hUvFy6-7qYrbwPV!w7#DHZNC#MM1^+puC{%?j$VTstaRe}7GU0Hej-{b z2!3E! zk%*krFK<7iw5-aZ8NBIAmH^?aRn>~H z;Dwd@ZBp4a9f~GmL@Jp>f!~!H^AAt1JKEC)6Q2zqk^&G&JSFy>V>IDFBA)a=>&<_Q z1%LZM<4?~O{U?ltEpLz^Vk{F6W7e5;hntQ=ZL&7&T_^}UR`Dc5HhpkX_trjsDKShD z#sKZ>xu|6t3d|c;l5y@iE4!!sA;>bCDox(+nn)t;|By90mDLJeW02kH^gXtsCtG%} z6janF6CdE^#kT2^xB})s(GnX(cF6y$fBZhz;gA6lVEoZP;OZ(?$gzvfsq&l1>|kOk zC2e`*Oe{A`-69hwBF=KzqNO#;{W`i~;H^XMI?IeWGOJazn438yt>)L?batAQET}wo zX?wN|b@%3d0Wc*w>0Ch9Pv@f2Wiy}e8d)0(3(=|}RJLfJ9UH-(sSOp&i%IsHYgKM_ zl_}YFVgcH9s8JdM%T_V$&xr~2t;VnGLtXedsF`TJdrMQPTxHlgRV@uHM*KLmE*f|< zuQv6;XZx8CE8LQ(+QUq+#GbeUVyRx7*qS-hr*=|nixoC$ys6NONJ~&4xCA z0NSid@+;Br~aB85sHL+RCzS*7#_RQKkM^?hhT!z#qQj+ zI}U^o&K>Qu%Md4=!aU}Vwf1z|!lrl@ZRa&jd^X}~KQ}t&Z>T&dh7R|K$NDTxB@bAr zrrtig_$c9;_hhcu7Cm^wg`p4jJfRI z&fajr1*njRiw z`lZ}EDY=`o;|c?g1U*Z8%Sawr&s5p^sbu|cF-LaA$4(Eshx;e^H3DdS6|Lp^Sh={j zzi8|nyC1;IrpS^Sr(6FK5PAM-YtDbex-0=bvpeW0KR3tJwJw+2vn_+{&H)jhgdeaK z&FP8^oDoW2GgHBVb-g4icf7O2NjJRS^Tw-fH>MpGw~8@AQUr2#)w1eL;10l3{3!1i zMQ=O-ntb{AT&!>N$-hQ%kLW%BH(>r6zAy+(k+{(Cs;7;$#K0ugrfqA>wj;5sQn^T; z|MnErM&w{H&HrKMTFVTLu5S~zBAQqe*IBFn@Z5?NK2X5&ymxjQ^OO_z`J$K5W%2^ek?5Iu9uzK*y63+4;=+SaQ$Kq1OkJE3+E%l;^eQMSNY(+D{Ffd5=8KZ^C65<-0y93)S19~S+v1V6V1}1u6 zs_OTQdgVY6$X6}u;)5HxTDBNO|Jr{v#5nk8fUp0T$!ty&otD}|kX!nd%x*1B)a{gh zrJP^Mtno%Oq@Vus;%QMb$B}E|+%CU|S{QY^`z2nLxOv#gbPYK%%AqYJT`?~p{Z=BS zWQ~T~{^yEl`8Z^j3FLXXCaQ7JEB`IMKnx=NOKS#T#MJ}5NTvorXVzb1fAmii?)+Cd zszQ1YIezy@F*{#%6TlDa%}rk$y8G0YLIc-~z-QGSRi)*ZY=HD#1;pE%MoTYiF24=c z0E3~}qDeb_J6Zqks?_?(NXt!_=9{V@e6&T43N!LS925HC$apdih9rbb`d4}9sh^*@ z7A(1c@A)(o_}BJl-($ewplp@t=ET0gZbPE}H0Abx84ZQt+DA`z|0@~;v3GsRYHt^R zMFVNnS#ytK^$T^ZzKkM4iK<7OBk)OU^w!zuiGAz2H_)o%hZB9a%pN9A zrYIg<{iHg;k-khpNsC5@xU$y18|$~mI2H~~_%~`Q9cA+T3=-oD2_7fpZ z#=yz0J@r$Yu3BBd_z?LwZ(xe`tgOvSKwt>Mb75vr^+&070B*AmjYuO{#n(i9oryzA zPBRJHXn<3AL&_t}(}I=9t-_wtVQw?C8bx|!BX{Ev81-k+9m|I5bkOCqI{5gGtd$ft$NIh8l{@h zEw1hU*W@Y^8S#hw6&25a&2nij81o-X56$!ZL@Hp#u@TIt#3xH72phhAxb@Zncws=K z|2`5}UkG1Qk0d8p2&Nyt*t!4pwrg4-M(V)E0ZGkI+@O6Q+yo=15;&oLm6u<8=lf_Q zC+L%-&1Yx6^eih(s{=UgKO|!OL;OcdTBL=hLs79&M#%^HcNTzXe@eZ-Q2LQ|!}Z=@ z?JN7UczAzTJgNZX$o05LQtd_2uy8{J=|m^Y_N>Zc6k3QImCYUA_$Lh%w@6;&@I>4c6If^q-V!fts=OdL*Cp4iDe@s^SZJ5lC{8c+*7 zzh%szDUBs!m={cm_^oV1pdbC${`4#JwEiXL{#XQBpaG)JZX|&v+H6b7mr(V`T2ogH zLZ%U=Z`z^8RTGhZ-^yCkmkqS)1+|<^#?v&mVFN9oJ9oX0l$B%#K1)F<_(IRnrc0uA zaX7}RrB|C)Djyn>izL(ZBPVWBD4Ro+p6xNMGVHAt_g$y9hkp9FTV2)m5;;PJ*n8wb zPs1?d&uWMpZYhT**0sD44WNu8Gv+);oNdry7YAW+{e9k7-UtQtKGxT zCp6c6b36V;DY%SafLv@5`(uN@`K?$ITmpIJMwOO zDu|bDA-9{?k_rhU;1F8(J6TTOb$3X5xLv6$%aHDG2SGPUnO3BcqvLQER5gXaEVZwT z0weQ#U_{dbB$O$d(#HH6CW%Jf7P0>tCbhA!;uOx%5&!K-Ipv_y$r}O983d)EcJlX% zj$C=$=ndXdT7bG)(3ViU9|2UrIs8+}fdMT*(llYFX8x9Sh6FCz;83vD{=rMzV9t<` z9p!^HfB=L9QLflsHrHu3$_@7WRvD9aNl4X<%7)CZRQT8fJQZ>%)^4P(Mw= z%b_T&V>|^YkKvc%_C6{p9D8V=q_(VIlH$;AU885`PfT9l7B6m6-im*$Gg6@AdN?DL zYLQH2i~*9p)p_c0F&QMTO5hRba7DIRYEP5Vx2N|NHvv__zvNiKCYM)R*gt|W#eXf9 z=09x_9W?l7ThxDvtD~cI$+oUEqMaN0&AO6X6g8_>k1K4b;m-AM;tsYQc`t(L%U-E$ z7W1mdqE*?n?LgmM##noD?3x`@uE5^x7941d63-@>tUE>S*{Ptpn;JZ7_x+))=gV&0 z@)+66y8tCJjzCkjfF-$KiNHs){C8c@L_MguN;LTWpusyX_t<{)1rXy4{x2!^w<7Xy zA3PK};7|Sje_0)<0b*jC|5zPlz)ji64@Uw;s{{3j3VyOjk|K_p5vm})&jM3JY~b15 z)O-?eL-7L1ZXCUZr8Xu@pSxb7d1hn!Bg1ladC`QhHI7bf-Jt_rYd}AalJ#`EWuz&~z0*1aWuh9Sa*+n}f!Ps|_nBTU5F#U|DZ1!A^GXmc~PFcGiAF(o<2SQ<*!8fZ{7F*@WG%c5`U}) z4gM&rf3F64isF{_465W93@2W`+Y1Slwc=FPdqRmg)vg>3*oD7RsQ-s=^#Aa$000a; z1F4A4v;F&Y%HQ6Gu|=!=&25;VN08REWOh~RnoK3*STSY$x~l$iQhEaq7*LI;Z)2(a z>o$yJP}ut$7oR&w*Bd@Qm+3B{vbX8mi@Q!pX7ozqjM78(t1s_G{+RknvH-Y#l7_*P zz!y2T*}N2}KB)VVnDItTILYQC1WCLyq2bQ~oY!jo*7{Nb|g!6dH* zPR;dtqN6%9Ii6{4N3a0FkXDLWC=X#P8U_U}ii270OtKLfHIe5PWGd~xE8@f3b0;1? z%`8~a;H@nPzI^4f&7Q6`=L!%0x(y@VFsucx@4xc?Lf9SCcLgY+?j*-|VOXmqPODlc zDIp{IeHq(E(YJh`eaMGP_gzzTE|)|Ut+|vLy>6rnI zFA2x|KQCE1Qd7I?$;%&TOs>Qk6(R$!xM|7vN;rF_8J0nh{gesBIWH- z1qi?(aNv92CWmJaCBQI72|1X@6qkc*QtQoU14bLPgLDEp@h?+?NCaNp>U2L^ql_!T#c z&j6jaudQar?z*MUKdexTRoz;P0alwJNLWf|KpR{MdF-+H0k0oVwhilyC2x0J_|3&w ziu{{PP9+qSzJ4>Gnz-%BCu5e@Tmb@y zHop{?apX8em@`f|s|k!&+bb_J6%AR4X{NmE`4jK#D%svkk@Y<{nSM__HII6jR7jiya90K-Q} z3knt=ibJp8LJGM6ZA?=?-&RncgOW|DDCHSF@o1uAaVmx^sWFn!K_y8`gQq?hC}rKd ziEsDFWak8-6sZ;pfFWoN;uzGHW73AkX$WsqfCqAv7#PzN0y!W}puj#4*u|jU8~Q_0 zO1Y;CppeU}%dbXKoq`FW&luk+*zu}TmbVd=B!}EA!+i1jP6n+$I&|D^@D~pUGU$wg@+F~RQ88c;y=7e zfh*M0EQgIs?iai+s=fB}MTQeH5NI6A1_gGF*mC@LncM++lM^K716kY8v;@FfA2Q#q z(YbsIrZ6ao`3bdp67ktf+#(J4hE#uFApWX8Mp|gYGObV*KQG_0CSx22Z(^xz=Yx$} zSuvf2_HEipu0Ascs-63;A1f(#NZGdY_SJn(jPLg@yw{p&N%X?SDo)TPq^0Y*y|9BQ zua}I~>31<;Fded?P7&wl#gu!Xcev@t9=sZ)ph2Dr{_&d%AY&LS6&?$17Cxo4o2bU( zI^)7V?DiO-mcHNjdFwea)4w(U0d&YVs^^%m^ljPu;9cn_UqLQ5YXF^JcQE5Ji(61@ z5rFXp`k^B==Rxw!j(5Wu%+IktQ=`)NGjX59wgfZHm$=-7`((MgN%9s&)fk!kZ#@%R zTR=%;sh%oX5N(ITYq`V>qftxTCSEHEts0}~VFmg@zhv*s_YcmA~~v>ak?}1f{Xs- zw)*~9IgQwo5484@o^n|92A1&=j?K1=!9+>&I43|c#iS*Qkm{dnjON5{RTCP0>x77D zbC^zDx|xoHWR6~B+m`k!_8}=2M=*yln*lYE*2NV)YY%#Yj)4Z5URhf%0Bw1Tay$9P z{o>M=N%J9qxnWd#ZKZ%LI~wMBrbG?e6&-Aa^>aU+gu*G&C|lk!>Q51$`5+`eZP$nf zZ61b5!c%m&Ei&V*f0nPkGW34yk1#RiU_X_VDv%UED;cC>1&$(U`|f3c4OVxzp|06W z+EJOwFOE*BBI*fZXYwc#Jxsb;e3wF1&TaXwz7^^hN?NE3{E-GKB6kSpZre<0Vn9}8-Z!{`RU%z?T z@aVII_sbAGgYMlEL%t~mpmHAVo|vVMwqmjy#70XmO#XC&3@Slw*~M(%V~G z5)^`4s5SQ|4&KILtr-NdNu<^O!s+3#ysME zOVz7TT^k~3+cM*td3qv1!QjF>GdisJU_DrDCl!LiaFUOXs*3qc@{k#z49hfC343A} zh?vFyQLiRm#z<7u;7jb~5yTG2w;+f7lJq*&)_Mw98`id@;~9Y4>$$cyH~Fz&(qhEU zS0$?KVosf`8X$a9|T9jT;4p@HWB{1f?D<*9FpcpX5o4L58n z6|?bZvC>NwQb(Km1B7;6o0#onk>X@=rsMl@1#RI%Lmr2DWJ3TtjU9jJD4K?|tlDVFvJ+rwP#3 zyD&AX8BRS?8s}4R`9QnW$!xh;Q(E}FXV5we;xYit5#Y}7{YP!Y2|{oJ2lJJW@Q+yE zVxe?vB6OODp%Ebsfx)#xv;rOZnh!~#hhcueQvfCJ`gkcacpU(>V?$l(kdF^>s+Hzg zza8?n2#?4!H`VQ<1~7Fz%<)w+^p1@rzyp*8Ng5o~C;-t0z|v&IG#7l}IVOwHeM@SGv0CRE)SiLN7q2e}f5IQu8ZhOibXaU33#7Cuy!n0J`XCQwESAzdxibwaF`~%2KMq61tYc{DecO5V7}pm~^r# z@dVEEArQsG$TFZ?nJQnN<5&d{pUPv6JTWu2&l-#5{G{Zf&g2Wols(dja2$3g9~|>W z9A~;=$1R-fzEmS&%3yH%Biz9-9w@{Xd?`3(D;<8P6LNzJ|0rbR3n6K^bU6zt?PCxV zLT3KA)T&j&{?BymHuf=iiwwXAKJt;KLTDN{0|nCnbHJB)m;lBO-cNY>EVzb@HpeB8 zK0*i3uvtVTp98t4nrk(mT`aX`P{%%*o&F;DOszV&Y!Go;m|QB4Koip*B57|m^LlOb zJd^X%00|0!3E?820m`^&BwiA*{)pC5LGT~=`H`w&J4$ev^& zelTEa9K}aGxOOsRD(Ku1KN-L))svldT=h`E6~NxyVH*3BeHnW`T1+bd$J*kh+NrLJ*N( zg`kD}^*EeyLzw*fk+M7Lxeyqzjv@XNz?@`*&~)|@Nso*fqy0g+hZY$sG+h!|yn_bu zB3yn+Lu@ANwn~a0kA@i1u=|*b{9tIqQ{~rTKrKf?ZIzR56diOVMKY%=ePmjqGHm_L zQrDK!ql-9<2LXIAOKk_t$chfQ*j99jci73Y1kV!v9*3xGu=cURF+kc>lB}Rw!XZ zhNg5^1s3{z*S)$0cggW6ZApm6(|mSkg9clwjc}J<@~~exST^ILG!y!bi4eM$&U{x? zCscG-pHqytw;=?K1WUxTF+F6lI{+w^hFN6Xm^Zr)XI1Hu(=+Tnj2mm;1ITkY3{h~E z#Dvw-uwwwCvHdutxaP50v@Me>W{yOy!Y~XPa*$r-`BT!*?DD=wkSnwDI5|go(TMg` zSeU8B%+o*eCQJK7l!hr;snNYlr z2XgxW!Q`ONb4oN+fqT@WC+~3T!ky%}AgPg??lkN&59_WbzLSS3FsiJjLrnydO((@p zbFdQ?2kr|KS>K^fe5ms(4Wh-x5$J{c)t)608~g5Acg`s zkqfeZgv8Onvor)i5O)qaAHl$G<3pMsBZe2TM@(+K5`Q4Gu&%ZhkQM@IOza`XWoNNV zySP|4T=1c8(0yy@gOfU&*=S&X3zmnPXTxi?@a<%Ag=Ew-&Yk$^Dw$Kjb7WR!`R(uC zt<9Gnk@%_r7Lm};xCxhn%5LLCX6hw=N z%@P2b9L)SK(>HXa3IEb79Cvp|$AUR1{aK-{P$Gc`F|bj1BS4>`VR~^WJ6`wo8qBRL zclx5skB_%~Os)H^rtTC2YYcRR5g{ZRASP&y#vOWB`uNP~V*;wDH4TU0Azv`Skz8;r zt-xGRJwQrEGg}mwD&YLqd1Bwx;|I_2H;0>gn~RSj*a+_$-Zu`MAzmEjXbr&_PlR zeNhNfBo7ra+J-yDUI;hcBTV2iehd;|m?BDS&(ckPke0+izhm(_k#2 zIgJbJIS=#U4TFph>Cu&A$q-q7@JZt6p-|;6fkfD<5aNe}$&tb3u@H+8 zpvwXYCmJk(2oFvJhd6=?>R;N^M?06zw=yx!H0U-4JRdwRph2(SCm_ysY4Bf$;UHr# zL95CFNFSt&jke=M=NJfEBD^sDpgj-diFxHp15Yx<$vD)!5MjjZ`W`iIiaDk03){|z z6X^i?5Hx{-@!(|+f9RK86?UI=f*SzPsh4nNBGQ2ktzcsh10{vKAIc75tZ6V8wDYhj zNSTijkiqwXPP#HG8S^;Lyfc~93x73oL-_^4{MkpInDt6lDi0=0he4dL8(sf!g!kdX z3Wn3yXPhdpM0{ezg`joD0yvOZ8dQ^km~#SuIRp|q32MKLaP@lcWPI$pPgiZd@Crch z;JpbWpS=%2!7B(G?z=HPP$~h2;eO`z;pEi5x^lp~8>VKGYvnlL_xvxB9PC*E^fgZ0 z4u|@9f7`arpk@v_`x<7wpf~dRIF0-M^p}oU8f;|cqci4h0J{vU|2D2v%;n3-%Jr#~ zS5uI^Lh>p@hyuvm7tP9|L$X1Q9rqpxz<~mZW%4cs z4z!hxUBO{1xVpZ`rrXmwG64Fbk>)Tv&Bg)Yy#!Q~v8y~xi2&lh_vg-w5Dh=v+N|>u zs#E%HSf_mtP^CaE7rRIhGbezg>0)2F*m5E8B&EFW8eE1C854?sGy+NS5)gTq{k)3y ztsrFF>>?kY?>LB7JLCRtE4ZDv^E(}- zKzB!2W4n>CaRO$UjJ?7J?Hj;s+M%?pococDStMh#m_K9p&Xv;^9ZC2}iul4R6I%|L zf;nPK!r73@_DCU6_X;58Kul!B!XM9`*(7E}hCHKtedWVmp(P`}Vj?f1fGh})90PIM zJ$~2418MnsW!d2b9ISnqNM;rg$5Ktse4jZFVqfyUf98Kt_MTr&eDS;XOeUR>gx&&# z9t1U115!dq0S$_Zh!Tp5h#C|XL2!})p%W{psG+E!sKJT|8k(Sh1Vu%f0W8=<5wYgv z`#a}(^Q`r(b)J7<=grKX?9aaL>wYViv*EUg2k9j%%4mUHeqQ+Oud#b?qBpu`%$j}} z@R~UF;Lx{*Mi|HDKRxNRYtt!FyZ(b@Zi1Yh+|yaa!)WB~HsKH4uLzqgGkE6R>=f(5 z&_r_J!*?@&N&njI4nF6x=Y&12{KB`WdRMmui5~&xYA5}hwyQKnw5{Ulor&hjFvfTs z|IPEJ(`KJua8;jJBt$oa<`-NT{$lW5IRWi|uh73sohba)VI-aZ+x0##N79DUS&~6H zu)7)J-(C~$+8I%3fCx+1Qcv9F5vAwPObc9@QuN|lZ12_drFT4vcO=VbG%i#aM=JC` z6LB0vI^~?SPp-;X$D>}Y=h8%?2Nx}MGYzHx$RRdY$18j&83TP7Z~fYz0Y(qD1SW`Z z)2yv1d@ASNi64!tXti~Ze_b^?G@jI+Ls=D3bH0S|Ce-eDctaJ8HdbD);}^Eci?!pJ z*|qzt2LR-?NxipdYn)UTlX%#={G61scu$^3Zk~{?48<~h;k+WJ_zDVWRAoYpS zw>FJB7fgc;L7a0`-fMyjulA}ueM42Ozy%7+Gu82T1$p_?Z8Hcm)`AvFBu>T|m=Hsb zA+fSm8Zgc+KA`Xidc1!%ncx^A)c*YES+C26znY7jx9xBU-FW=*?8@cW+=Ne~txGkU zm#Ds__PFcpm<>%cZ@RfQ_7zg{GJI>u=U^izlxU?J=8N-&Btcf!lU02SZ`a#wyY(#G zV@F6Z0#{LJzKXo5v}7YN>Pzri3Tiv9hB3<>>O8Xix<}(p&Q|~kbrFM$Uyw5PJn=@; zO-2+~Rp*(TT1fK7HeyN(FW{An^{DrftItB>?wtqvgm2qjhPI^nu7B$t{?5emV*XJ!a{KJmMFqvnH)nd=@|UhzLvB{OCTl1%2>yi7a42*(x;q!K z{myG==Z`g)@2A?A9A9w{RsZANy!HWipT%9Cy$&-1#uD44s$#VJ-seg8)4n_Ybh^Fw^qGwGy+dzz{M|RQ|GnXp zQMLUiA&cudym{N()aEy@`@%8~NHx~}O}0OC@qX&3YaJeGU+%oWpH_CQ(SG~44lBhD zbQo-^49KKR;JgO}{5Et10@>PWJc-^iN4)GYS@756lE?;jt- z6ZEpzG%4shJOM3M)W80=^JG`VKAX}S-vUr5BcxYV5}hPm+>vI|p3mi|E^d&)-6`Ig zSL!7VRhk&rG1VoJ(Z@y_%Yua`J=TtBh(xEiI9)s0v%*>P@FiCJoToeqi|U4qf3pLqqZ%r! z<3)%?SgrzT$;NIKwdnZ+R$lsIqQwqpHE%}t(qoe)EpyE%4yVJUh&ARmE`Umv z5F+d0kd^cP>Z0hQmvl|yIm-WOlS$=FIe_v084zVJ1rrEM>K-2VvY%5cUL6Pc+LcTE z)yHdIUSC}o%Mp0~>LX>$f;fd-kn%eTFTZrB@n~X=Mh+9iQP5HRM@o!H3=B=%wOTw=H2KGZ*$Zo`t=aj*GcwRd`^J)F$fha=vBg z5=bKjVyN4%q_=Fo@quY;Rb4rfp0XV5?+jQrFXO$|HoulOA~0TSuxXE@DHJ#xu=I^$ zDH$Ee1WQu_h?_Xl@!&E2){zb)U8;IUTWj-DI$-!&jjPuUY$nW2^Ud7GG%lvJ2Qa*n zFK!qCJ9;D^RprQfX~DraJ`deQbr*$Hv+qR}9Y{Ow(ry+XBbrbT6$vyKNueao@dYR^ zJH2QTU(XTHd!mXY-sZ@ryf^oz|GR#?u2F!`zjJL#_>Ei(r8n(n?1PNsH*!Dyx%PQ? z)kAL~Gvmil6ixiXC&2&Okz5vg6=ojyUFxHP6cS>6k3BLHWnKATH@@Bc<>GGCtt4n+ z=GTYLSg-JprCkT#p*`oXe!G1JbTlC{YDM@JQqZn3RA7Fy8Dqr2Sgj)C6{&0x|5dlc zLvEs?6HHZ+(RiCLUt0g;c{^=yg zCsS3iEY;7#Z)N79yoNvE_OR?{W{pW0<4h2}N#Un%zVaviHUFxt_|F@gLkE1~?n74v zO?q26$X(2hAH%L~KWlzDMKxXoX@)%Tl{VE5x#}=rA|w0uZ@{p#mP_UDVe|KQLh+qh z9`OQ88pe--UN=xYF9bj;ls0fvhpu|TfZu$2sREKr-2=Z&+^37V)njhD)m(kCTlXFa zr#$Yl_~vwe|K$hITOX9xyCU8+;8=(Ukef4u_x2NG0UIW=prjOQe4ty)rw15$_C^l) ztQ5IDKi}}Pmjj*ALAZHTWG5ILhz_=giOXUA;o2vsFa<$cy_L$-M)@W- zX0TLg-}1E$)N;!al&AvzdF2fdcc zO<@-HeB`^bF)A>CWo55QAtA6Zp&CwKmK{gu6R=!;Dt|A>FQL-{!lHNn(gEf#aIx^7 zHY*^$W_=9cDiOrEA!67lLk5UWh4;akl=y&TTVhmqn|>!2L4$vRT)5F=u`b5A6F@M~ zQImeT)?7sz!^&{p(!5_R&4+NZ*29A^J?&w9nZLR$dy9}q=5UvlyRUbF<8;wFf#~hk zJOcA#O7FRAaQqK&YQ=1=5T&3q%J~l6(Y!|I*uHBxQfmjH^cHWALE#TS#h1`v0XW3 zos{ZFr`x#t%KTnM(Rj{zN3pnmn=1730&z&j_?|kgW4>EcIF`Wu+XbGSwz;uK~bTNY2H6`%V$7l5EgSW6^x)} zN5=JPgj^F$3N}eWS~%v!BtQK_F^7Sq(2=`1JVOy{2brt9OAL;_*HWOumM!CnmzvUH zuN1-dh|m};HyFUAb1>=i0cLO2)ZX?Q00w%m1Rmi_EZv`~hC_-&1DK3S!J+DGZQV@U zLP2^pWG3O7Ypo8;g?`-{N_CTLe zkM`;o+g1w3c3^qDILl>#5VQ@wHvBSY6)kZ==qwpdYgp^t^MYYmi2J!Vf)-2^L%Ze$ z(OLY|`rh-@unKzr%VT;PIa$lU^s&31E1eN z4QV*PN!Qwkd$j4xkn6VLjzX2x@9PvL>RgYLfO$N|hk>wVOMl2Co5K)$sSqoSuOhmY24A3Ua>`nn1!kPTszqGyLc{LbpL5WM=R9vTSA;5dUn`3uLy=Ba#xi z=XHN1MLx#W*uE zKlO~3J>8%{9UoapJG@*2bDt-3Kc-519cWOT%n<0=b(0qk*cgUKq)UZp0CX~rnYS4dDGA=S$RYp?lOFB5PiwW za@@nk?5xw9jpIHa-Tk%#0uKq#F^8A318Ks^j+OBIZ}Q@+bXRe*DV+TC^kdt)&;729qYU z7RoaZk|Fk0RVEpc7X#Ur@>6+$_dD26h9IRtexHF|;n@*aF&0x#Av@GA8nk2p0hGRs zP6^QFV1hW1`6Ljx_T51-&npOJLI-CVz?u{RopF}aZ0vIvcDVb_ecRTJ3Baxt^pYH& zi!P`zulBQLA@bX~dlo^?oje=|y>)=+$nq7w$=2>HU43{~Z!E=R;Z!VASckZ$DZdiPn# z-QLKJ0zIm%cq}kFNAR~HQ!NLh$20N7g2%Dpp;GRWav)5EPERa#oOI5h2^?YNCkA@3 zR={NM(wl~Nva^kHFdkG6W+gb2bie0>_7xGi_1gSLs40lt%`^_pTU0sT2x15b!Nm877qo9^2NG1JW+*8}FF z>{J@wcAnTQ5k#gesAn%aQa`0Psj8PN&^u(8wKQwlVDxl`ia1HIYXFnV$a))Wn$ocn z+lr@y0C1&Z+DtiGm7@&fwT+EYcPTjt6+&8k7#W^E}r|q6zjxpKJL6DHY zbCc8Or+z*Bo_Nk26ldS25zRHT_v&PsM@?(Imn(Er!5CmxuLRcrHnT2w->N0wS7P=| zp?{WbyRIPxgeL(sa0xF2@K`QcB06y~=&o;P>kKpVW~qH;sUsIgGRdV|fg?YtM|c3D zv;ZM|Twnq?m&5TlbxIe*CtgRRIjQFq?bMRgk_W#KlT<{#ZiyQikA)E;-OeMnmDgZ0 zz|~_Ms6N+qWj~i%0^HV7yJrsTbGXW78&(2XcsY*#8yugqE&lzZeDP^xDuoTyS;6FL zm~!axy^gm0pCuZlCQ*@pG%A1RH}7s22jxF{n5Fp}3EOJ;0S`U7QTjiR9Eu|8Vw~zr z^|4$>mrnFxK%Hk=Y>7@~$EX6DN*jub#M67OV!+Bfx|rn$NimO(Z>9E}1G^5@dSB2? z`}sScno)3)`}0*=XUz4EU8SSz0(pes{d&u4B7fEt+kR90`M95285Mc>?K#78c;fx9 zSyK8(X(}o8AKqIiHvUZpa&Q`0av&@EVF427oCphLZ3HfIW51AE&g9~?QfI|`t_G_B zrU7|XOfD11a*Cw0Mc4hi$R-hIv7sV1LdV2BSQ2i6%+a${jjII^NPoT+N0|6=*1ErX z)x}j;6232ZQ5$h*cVX(7_e=3Ay%?0uobBMvglDHV-n-q}=wsr$Z}Pvya^viPoaz$o z+vvJ@OZ`IITUW*uZ4uT5;#p;a1DMF95JnFvseM`RGZ%SW$ zW9OG&PM-&^Z+&)k$8YDs(mf!Bi9iM(k$IN&%ez1Xm|0)qP_mqxiPvl97x%I5x`g31 zDpsys^|v7!!Z@zoA~UGxj1RoLvCDJl+=C4HTPx=0e;)t-{Q5ET?VItvbOnwf%b04n zI_LRat)9wPb)BbXsx4=jWPLScob$x8SSA9))OnK}lRSoLZnPfFINjn>y=nf!hI!Kh zyWM*$xcJ8@W<@R^>&=SY?+Yrp`o9?FhyC;qm>;oyS{idC#Pz57vC!o|%?h1Y9I!aC zKH;v#v7jyWCMD~l^IR-XZcd%tjM#GNuI1?+4Y>xV!22#%XZ8*ruv)eWpKEn4{nt;c z6Y&UFT6va!YHXQcK?AL_&;x5+CAW}pE00H{T3@KQzPSxS3_WFCU7qKfu)I;?qHzZeZu#tLvHE1Yd7zQL=WQ1$T8F08f#GE6ER7|^aWfEW6}j%ril&C| zp9WkuY2~P8U5{zZ(^6<;vYC$e;`B}hMCk)V5k=%Uag!Xu^KL8+gPESDiD2A-#(+xZ z84V1=sh+NeX&JWXZ^s1B8ThCrWJmg{cFzQ;ppi1z^_-nKm);s!qRuBaSaQ{0Dzp4V z0#1VU*wM%-_fi@r&#CuM#;!D`5>80--2dsCqs*94A<=Tn`!`2f{?-=3LrGivfcBdR zv}sOq=%{itRz|LH$KF2&v=UJlN~uasF(q&>C20q`NO&Y7P)InM_1G zSIiYdX`i2HOt2Wg%FgE(`R4_2&3u1Rvxlqlbr$U!3jfADCa%A;j)Jj$R^DQ(>L zLcwYU36uY!vY8y-1=Ae%WEGCd`jFK4tHDcjN?jj8;I+j1>i3tG8VzeI8om&xLTw?=Js@LA6K zjHcGABqTXR(wO$Ij`clqT}LjEwyLd+tTP<_TuC4;`P}v4n0ZO!^rO{lbGd$$()XQ+ zC=(zjrQMc2fw|ppYNSD?df({Tu^Y@!Io&ApvAH5&rE)+g&d$-6O#!_qef_I~GBSCY zQiJ|CO2(VH#Vf>pzAVNJN*bN;lc%Tnvo~vVa_!7Yhw09v*~S?oGY4Aw z*p;Mv&4gdcGJeD#sZAyBQW@?;J4H7lSm-3us23@+4*^4@i;mu0& z2BDuvEez2ndD`Bb7BoY~y?JE^I$MlZ9|y4;;(U;pP9>2`=j(7_q#?_TkFSnh`H9R0xUWdUc#AbcO+v2UNEv01aB@;cRKfdAWDl~M_lkmf{vJ+@yNXM)sR; zr9-C%!|Z4ObRyj(MXq#Me<+^6*^-IbS1XF{U}h0Dj|!dj&CjeOH@6mmsD0IAsKuOY z8x9O@6!S1{0}^!UWSb7bRL`8&L0MF%sxvt&L`TC59&-_R8qZL>IxbA982o#8BCOAG zc!Ab#AX?YQ3z&@Fs(%>rmpEdAniGhw&jcR8i_p;LAd#2!!a$kzpzJ|36NLpk>YMFN zn?r3+fa@PBo|fmz5;K(}nDCS$zRENf>2RWMfG-@o(`@*4!BVdRWb?ItK3W%n|R@>SKGR+d~ zLjN$roN#rC2vt)|hCJ3->DG7HDW*x_HN;;77*F*-1$qM@<(HD_1UXY6$7Z zy^EZg(4)>DT?5i{cD2m{b)64Y2FuacACBuf$+(Lm<062`V@DRVeeDyeFTE1Yp!lH= z1WP7p-|s_O%8*cRZXMsM+hPEH>dsdGeJ_gw=spZTf9zf}NjXJPKc4l0NO&4!^_i&v zXG(33E0V^kf9ufh?@Gx}^wwLnRTz!IP-k4w2E1L~5eHg#4gMK6%;JrTW*4|3^tlhf zY=}9E1~6asG7Y+?d$~q8_x{)P5GCN*27%~k?UE%)rj42QD-Q(Vv#)6)UYNiDD)?j0 z+GG<{;+Sc>c$h2nn zj}mjp4c7P>2pM-rbJxGWo3wKzS zts>o5%=KK38C98A+#ow9h2%w)9YfhO$NCmB8@%JiMc~}1)79S)c~gCdD_(x#lq|KS zfZ3ZIxcKSwyRbjM=0x?9w<$t$ZahyUlTs#h{N|hWe3ya?>syJ@x~(*mZ+r)e3H~eH z7k5xdWOCSQt2nUk^kFP5#Ybn&yqCcOGxJR4KJmKVPaR)J@}xvlu7)E_=jsF*H7y1q z)h)WaSqJuOn$Bc%viD$`3ki;jpRshnrbP|J#Iw=L;kn{-v%ThWJYYdA%l0Fcx;fxM=Zb_0W0%sKez&cUoH_XLa0YuMoB66|&~ z@aG3cqniA(Gy@CHRr?^DxRy@;@972fCro3nJq`)6s5JC3}VMpdw9~ zh-L*x0c26;G6=0A#syYdr~ptideu)b zswvx1MF=*&jG)b3pZ{ zj5I65yU7stOagX}7$yd9DfW|X_5r!Aq<3U&x+8HyOq!+R^`vx5j_MzA?ozVgf{gf^ zj_1n=Zgd;}E$JGpyo&?ijL_wuoFrgDx%D|=5+H)9`bR_>7khl_An%V?{VOJwumHY< z_*X=ll`gPwVISUiT7gCOF63Eaz558 z?*ao);t++xWTU|33tZ$A8R-KH6W}ovjUMrj+y^8 zJ>-(BPi!=oS(1fheH^%0w3b2z%|&YHzQaABx-nZx$?qp0KS9n)qT*u6I${lg9=6GEEp|%n)IPG0^+v^Yjdcf( z5xkE4wX-JDiJvBpk;*kHd1QtTX*m)Z;#obM@Be-6;*md1WQBbGckIX6&kETIFLpRa|u=5ktWu{tH|~ z1`T4w#J{cNl^o(lA^3My>3>ZRKQ#q#Se5x~+{CZ)RNYYn}?^&;ewif!8@;8VZT z|1~|lQoC`otXofmI(8Hf9&3M9*1!JDi=XD3S6Nv=2F*|DR?WZy00k(tHPCYOI{Mkl zhkL~K_PFx1?V*4=%erK}dTX6+-sU~TwNgKTDzeoF`}4#M_vh}*}?ZaG>ZB~6p7bnlW^(n*P|%2)Q7 zZ#T}_PE~~_kTs;xPddghPfcA`K|fVaY6hedRcD~QHd8};44kecy`mx$Ts1YxIWOc0 zR}R@nU+e3H%i4r}8wSrCF33-(o_Yp?YlMzD9hSNZHSl#Eko{DxP-{y=u9_x$ZAG@( zDG{gWky>7MF+m1avx#$TNLAyKOh!19t+sq!0*1YKhX}bEgD4%0z4nV(=ZaY@g*s5E zgO+MX7a%rIEiPuO`b!|SzL3|M6_#GL5A$kQegMCaF`5f%PB(CVRg?Z!`&$YL$LcW) z$u+p>3&ZSXTlH#>UrIS6Q{BlytuDDrpIY3^RvphZE3&9vU0$0oL8EwqBfqYU4H_9o z`wj}B{AHj96MD&9QZ|2n^GC2|a2xbV6*W{7vEWMo$8!$Wa%JvIer` zgXE)^DrsI|)$y9D-KrPiVvD{@=yJq4g*lp$L!Wv^yfhG7mPE{Xd&&F=cm)C6jEIn*LMnmP{T2ApA)TQ|g9D_*0O*nZ7AkFZna&h9jmy?DY` zmblLaG0Mi%BpAn*)#`OO+=uFmyXqqJZzsf&^`0j6gJ7gc=^MyO-nuDRh=|hN6bY)V z3*SET-P_6$xc<8?Dl}_dijL(|=a!=`e>u2|IwXaZSVl&A(NQm%B+PA0<0PR6OWY}j z?1hBitR;Fd5`WUA61Wnqc#rm_f(OVhotGPTPml&}z+E!JrW>R?13;auah`+aWGkOc zB9|Ib$Hwckz(ugN206W&n(mQCjyRG0Nr?645Wb448o;gTG6Y9P6mXD4LUW*V?r|*G z4XZ2?ZC@!@T?VMmN=YBc*zgT&9?2SibwLb~_(-LJAyah`1xr8J^y=Oz;%~JmnJVVC zs<$Nn1vB^eQgm~WTe_ra@Tkj0xUIF7lrE*O_=NOjDy46&??eWgV(cCtQW*!qvTZDp zQ@7=kqRHmnpBmdbfb~Mblmxd#452ykZpTmb2%B~{LLV+S9R!J(wH~guO0AxqlN_4BP9J4cWnRD7+l~YPlM7MPuaBKzsO0moP2(FtL1nr zvytR&3vR0IjwYb=Y)K8=s((`AT0L*s)g%tG-R`%mRJh233|P(*O6b7Uc-0P>`? z`E)mPMU9^OI#vRY(z?Tc*LG0Fs983ODtep+0J~~C_8bHXWQ1z5s;LA}P#%RV*A#;R zLgGPA_s4_SCDaGAX;>wej!cx;k4woR_uqBl651N`hdTi}W<*LFS4ftgc6y-TkYIXH zXKlBU>eD~02YplOI0>rxHsBiPlPmz{o;NH;Uf*{Zu^w?<`#Flu&QfT3hn+!$k&V&= zqT2g>)af7sAheP}eG#Z5LNZy1!{mKVa_lGv<3UEh*_y$mqH3++1|jmqJq-JIfUz9( z7wIl`qN|G#JN~|4UPM^35ZN$kClx%!CdSC2H2}(qNf>2emPyebGVr|wXHCY>!YG!E zcru4{U53yQqU7r&i)o-q?>CpygGPbT@xPcG{V%eGq&hR%HbX-U zj30&pw#Ccj9WRT1s4Qb)YWG8XKeHXAV@zN?mje~4zB)1jj>z!ZY-kG|3XrFM+;}kW zdJPuPe9csn!`OK;#+{vEe3q0!AHEyHJ0t;QFkV{*ZJ{0)oPmT7(cgp^sZb?dN(^BU z^EiZT?3Ee>6%liEIDO>(nb%tiU+7E0bIIc-SI745fYj*-o+@*aAW!=1?^lMEh_joN{c6iNEkjcHBgOA4CPPBp`0wpiDfCvZ!Z|yx zWZfglc=E{Dy&~v|jIcq5d&>s7SYnDC;x0m#eR(BPMTj>duct#Dn^09k)kR`cd)W&z z5#|1G!s{w>l6){rx}j70>f8?K`M>vDI4D=~mvdhrpD*e-VfEsE{99CLtwEHr1@>oO zri!#bN)505)@qNHcF4v=EIkp+sszb|W54w0Yk)7T;$n`fD@PWxVYRPRZ$At3kqz#w zAe>jQ2DYpjIJaHZwh)p|17BIitzN+RS`0;8qsKNw%PNjqnP-I->y2Z!FiA-qWVt!` z09HXBBb-4mK5lA*D>G^Kja|$^&Qh1pNL4}Uj0@A*-$aH?z`&!Jm-1Vd;pVBD=|N1x-j1P*Sx+Sy=$w*p=oMTqz6k`e@K|k zbgp-TY~MqA(%%mj)4Wz9`elSw>>cr~^JMBw*2dd|ZNLF#3WAOZV8#j+{(qW^RlJ$8 z6~_s+a>TIYkBJNspyr{6#!^}wEZzE06^o(w4`fA$5K;wC%gSWPHFp9m9fggh9edZM@@WH z|MU2zNWTh??n~H2DZhxu5bY<*$aeuM;3EKt$>VclL2kB-_wTD3``R0XR|ZRFrjF^_ zs86XI1s^jzd24}ILO=D!SG*e7um3O@GtpjzR9m5=P{W3|$?Y_x68yLg~9 zCP`cVbTM($XPYfY!BU#P5G2bobu87Z*>F6;SBRrie1XHDO*Oj-g11$h;k2jx&+VJ# znhOdF2FfeF?kAaIQ14Dos2}?C^ZmJu)JN7;g|4TV`TI_lCirq@lQ8X+X`0>H^p8}o z#?DLt%q*3AMQ6{V8$MB(-2+x=Vi541<^-x_ahvBY>a<8usbwcbDgWWi^_{gad_g=`$1h8)Drp` z6_I0Y@{H@-WY=N17mhiTG4}z=)CtSUI?Ew8H^>vRlv>b@+b5Ou34x))*t3i;pucAVQ*8Zjch*dnN6v7B(zIMSW45>c^%fulKJ@R9nCK zBnpO>Hva60VtiaSqcF))8@0Q5gC>$>hRa`RVayKqZ$08hdV2$XsjB_44cTuphijGE ziacd_WHgsAxioiaaQlsrTV4JU^yXvfEpuAo(SDquO{rhn*F~)u`xCIDV(9zUQjbY5 z5Q#mN^fo5Le$xfjJgo+hP{HD&us0IqdtJ&?*BC)kWH}07 zzuI^UkFOC)RoFbr%Th@Icq#`s8+dmKs-!BSWs~ zZdh)8KvLv=B%0uyGoj0+3G7X|*3GwIXuYgO^D?$=&5kDs0~s(mS#8aXmm;hrUZe8T zY@?JaOCu@Ly$Ti_2xkG0C)YYEJ5u!ZZEs%dxg97#;D2rI9h-ng|K1%+A$(SZ7-TC3jp815H`uTpjrvnL$p z8^w`OMtaMv8|_aB7(=TstcO-VjNj7kD)XV>3&J_A>iU6IsTWwq&Nn3|G*}ik&;jc; zUM!@|adV4>cOOzT!%lY3%6z|-b{H{KF9W4a?S1pT@OX3C`eh^PfBQ%~OPwm79E6s# z=ucwnc`Cj=T!&jMlF>(GNx}}(MZcPTGdMnIaW|+!0(16>kCY{g1{J8g&(QbhgoyfW ztu7Ygm-qJ}&&!}4X;fC$F9MVK-Qk>>^O#6e>6YhlSU+j~=TOfO2SShFQUmQTrFIc zMNbitheENoH10}GX`3@!*h*wTlyr%=eucy$#K{}`gn`!9JWR6bkF&4h_!D=FH^!vO zkGv8gcFMyZUYM^T(dYe^h}jQ)Y9Nv}(Bbfqo|8K4g_$4=R8yOa9p-C_B4RO-nVR}*iYo|?Aq; zA9aD9LCaC*W(J!jZLVT=W-P-@O&Io87X6>9TZQYFyS)#Ivu9=q6S~mgle1@;4yTenMLM{Q?J0#r zi@fs^sUl>D(M^A|KE8{nlHC7rzxH)HVr!(CTDZ(xlR#E(rankh1|DRb1+|XpHEmLF zIqz^K8zVB4uH*z57f7f5-z>pjS@Bl3{uX$7oJ97oxWRi8U^f!B)~SQbuVk zi6PB&%6N7d(Kr_PQQ8q9WEQ6WnJ^5mR#(SIkwW{~ZkwsR9Ji^$LvkOZ{gcmKcE=Sv zaA1vZK=b-AYC*~$1>dMd*UOElhVcApf~+V$MN5{e6^nTK{h9~$ulD`gT?OK&lnvdf zq9P{p(7e$k+98!p@`#Tze7n+T>`OT|(FM|?$oXyu>AVAk*DG72djIA|Dai5Bza~2~ z^`)MF(-84eOfTeaGLfqWXQwLZdpXKP2+#h;q%kw~=$7%i*^(jL8?z>THe7;RK)suF zi0W>Z!c%l14TbRYPZ%Y}<4(o5I5Y-f)emAJGC(BeH0od%D2bXala0G22#N?Gva|L+ z%3P}6a){cZr;B`bS>nK!Ky6BthO05q_~f_SjKs_EbB-0wt=ECS)ShWI%rny!Bn@B` z2N@_Zk$I@TrJd2#55diq=yb8bgMPB-H)fCQ_#DG~ZMC3v=Kf!U`^`3utq_+=X)!Hm zd^Cf_le0IJay1y1Yoi~DHIp2)xIir%P zQ3|5#QR&pCx0TJUtxQmij-}^+FC|6;exXb+EluXXdk=J|*=q_UxUdH*-R|aL-j)a3 zlmj=an~!N5=;Ei3<0ynD;$|_0t(>N|6g*-)k$% zaUDnGuY0NUF(qS{{N1AP{gROr~JL zv%cn3uj}fx%zc3xI3WR14d|58p!nr#Et%V?h#Nu}2Rz0z&Cdn{2rL&bf^k?7+v!e? zhAXi_I1x<7ayvO)%4%SvL7Wnm){seVu!|5ddErTdfCnRGr|RQi4JmwW7}kN6@U-9A zH{_!x09JPD6fLjb7Pw|pBAYu4HC!?bl#xYSlr2riXoSh9Q76RAaJoZh< zKU)hgC|}_l1KdXj>9D&hW#onBJs0EP^|EZ_->#CRZ^Fh}A+9%&-`uA#KupN~BQMv7g!&)x~uutWmAA#=w-ON40 zf;uvNqSKR`$RNelH_UqlPreek^ydt$Ufy&qAgEYT9KHNj_Y*zl>IzwpxRF)Z-+2m# zO)leNF6DMM0zT(q|3W(-i|c2E{XSRv7hsoOS<0{P%9G+rKLAgq%Vj^1nf~YO*-3N= z>>>XU_G8}GJL|c0clG7%&%`~xtJ=dMIuNp|%Fw0zYA zpRYj~<8{I9)tHPnDJ)o_QBfc5#bvLuY4c8ws5fdqo8t4gxG25hTqW5HAA;H58d87bSG=*+xp9uhPKF@ z!TR>0#^*y#Z-?%GA9{cq4)=?w3mKF+4Y&FYx2+j&-#q*fb*|ZO@cQ22N9Tth-x}^} zAMWm1(|Kj+-t*yJ)QD7Tq|ajH#@peRg(LlIMxJjTd9imy>bmh!`pB#EBZId_hE#{2 z-P$nxc4YMXNaSnOrkcVLnZ@Y1)2NaP{*g0sHDvVN-qH72o21V-_7;wQyfyl%ebap< zqw#k1>-W)b-v{5KUQb!Po_2bjvbkb51c-HiJ-7GukE|%5wBV=3lkbJEf49H>(=_^j zW#$EdU;w97A_7>-f1P)MVo@_615zXFSthLlq@i~vEor><|7xZG|LRXqWn%6HcvAV> zO2cPL>HFfS3VjX#`k6bsdJ8ll6{FyK@mOmPT5UnxB-ymNK+|Q@A4XE8U$MH=+Rr(4 zZyq1E_Boqav&wwA&~p8eT9-W+gU{IQX=(d8eIe+y;~}E^phJ9Lm3QGZa=B(rZ#C=O zrsp5~6JB0e(`D}u8_UmM==tbm1^}3|j@!oHJj}$|$JcX{#(jtH?)~;|Wa!7@ zjrUT%Pk*=hA!fH{8DLffm)f!0_&Nq>%Uq`4b{t<4o1OT|ZgbC(0Kp1X{qOIeoekN) zsORL($rp9ouVuLZc1|3;w6W`U&#yb*M%xe1Sl&PQgY&*2E1=h~%yp;iWwC?jpWFBL z6Df`Pif_b>Kl9V?9tc*h%y3TwCSP}K_4G_npZU>y@8W~k8HfHTKKvNn@H2OvC>3ha z5ph*am3+q+3uSkZs{cGYOLKWnIZuCIN;Hp}O&iZKKM_BkOS@1#o@aY|ay*~@kTy}^ z_#%Fyknyg1qR8z(!zUAM{YL1kz3t86K+BppM}nO{{0{~7_E^~3ZEuf<7hzk_@uI2a zC7UX^5YeC_5JVx#90YOwa;n!U(;P8~HL;g*$tI2QR%h4GDNoL$Txsiq&E8-7eEV{YMcwk5^Cedw=kssv3K?6W?vd%;tLs)(x&>=h z)hRJp354R8u47!D0F$dApIas|L!Gx5In#TMb@Kzf=xCZB-z_G0>_t)!(~xdLw08I|AY zb@t}$Q1yI=RQDy5hcucA4sFl7J*GIjTZt2UPe1Ya9Dbi~h-}}p9Iep^7RiPwNj$=4AsBSPptI(n^OUOx_We{>qqy*I`X%_k*iW&?(UvJoVE^ly$Ch! z9hz&qVbY|_#6GAJ($^gR{6uSo+wLd29vk}j(gaxl5$WGfu$_@@u7>vrqf5E^<8R8y zf9G1yVWd+k)IbyCT;Sz_ux25nUlP5`1#ps-XPcLTDlECb?GZBW;2$q=Cgt3Nha&#= z(iXHQo3G*~E8O&7XmB1PQTG++_P|-FS_t9y&M@(f;$X}lS}IE#m7c_T2_{+COk0seGGm!)^vHQSSU|Tm2Sz(G5l+sb z80pcoMvA!VlT8)+Pk}hf(JNAAed+VMu|4^Q7 zcQ~4|_B4#4{zO=C)QK~l>+dj57~yHnn(se_?~j)i?E+y0S_F9^GH?HcX zgzaJF2$FfL>uyURp;{$I{qhN&hhuyKS*CkG9)xs-u+l=cffwHl9;qX!>52g4e>4db zk-+f5FXDjEq=cx#1Q3s@D5LQtMHU4X9)AbMs_q2pQb$+oou`6JPMKtp>jC6qER){9 z*#UR|8Oj6z@6UCH+9Zis)0avDKYh%Q&*j&t~e4DD|m7$ZP?ii5zTjpb-G)3|uZ@u=GV zNv7LDiDt}-w?{xWJj#|`(~M^x)V1G^s{FWVtS#o1!dxH*8x6TD)1WEV+lYyQKGWhB zLWt#V6@M37ksFTW2BXPBfKqZI4BL9Ss2%dE4yrl^@DBDi@8x+0XBR|)ZV>Knft|tNz zSKpA%W{H^hrVsy<3&S9?|YPL)VdPQwv26!UtQ`gYi&70*HmmABAy{;-= z6l(BuE2(o1kqV;|57xD3>)~HK88xHu*sFT!Gs~)~@K(Zv^g#O-%)a!X3fn3e;`F5y z$LCMARGxFsQGnp+TeDVz9Rv#MzsC?tkKNWJaY?5^%v3gtA__y$$*-;nb3e<`(4$K{ z$6E=mvPe92BR#0WS1L^`c0dBe^_7Viw6D$6`-%-V6ByE(C1e}pX`uIrT>)Jw&3}U; zDtB+4{`A4*Z9p*6l%O`xm!p27=XGbQ9k_bzlhS}!x>hG6oEC8goIGyDQs#a{;Nce&PM!d4de=8R|{Kq=_^w$9y@H zDk6BqbV>+ju`SCfPyZ#eK5WgiDMdo$>Xo>Ei(Y;ofK86Y@GPXEwYmK2sL@Fk7q4WHfEdqxroD0bCe!K^DbNoK`d?~3k2h}bD<-<^6EC$HVS_&Nn9F=Sw90-Yg4-KOaS_x1FURU0@{mtBu3YC z8TS77nkRNoh+)x=eeMkIqVK-r1?_JX2dBNbOm}{8o37CipY`C;Q+qX~9=`hon93Oa zhY;qoC<9MkTum8XiT7lH`U@CWV^3TS!wfBB^ze)MrVWdn9d9vaS(X&yQ@7MmDM;AL=5T&XUdd z$p27KMigs5iftOjzE_x?PKYH69i0@8n4}!z6Atg8IQnHD|3Y!9$qpuFyUb=sf64X| z&G8h?_O;J(i_Q*M&yHxy!Kq}&3}=TSa*wLyM0Vvw@>z_uOT8pY4Yubp+@-RbB%jMl zk(#84HBy9VYW}Qb;r?E3akNyaAGO?`nn|UWMCavpQ7eaYFNo%m*Yk?(^XhA;6=`{O z2+7|IvfJ&l0==>_`~^3M^KYmW+%qb;zb5Sb_noK<^AhA) zJh5&dIfh8c>DqUo3KR{M0+W+$!~LRhukg!V4+ZZ`%7!GI_0ShoPU%{K#sK0~esWJ}Lo(L-I%JEG6L@O16c@XUpHOMsVwLVm5g zw+5c&hd(n;f^9m>GQBx)z^?J--3PVtW#~m3jD!SsjL)If>MxNr&SJo_VD%at{1)?k zN-d@;PbVJB^Ml3vivgEtL+s#Eud~2r0FS9Mubg2y`5x~a7Mzdf-KF!a<9MSm`hPLJ zZul&JyeZqOcO9#}1Uf`X;glb0U}FolK*lvl;;*)g^F8{%2;iv!R00d^ubr}2+JJEb zcj6n^2#W~g+AiywBpd1~SJl!+e8EExq4BQ)u&Z{;4?NrLNfO*&K>cDi{;GwWe!q5T zw9)+Gji0a`LQQS}Z{EWu;r#ltAHuE-9*lBg z^TU?BW?)VGiac2Q8e7-db_Mc6qk05X!s0oL=kRAhZr$Z@q$76OvX?M~yEfMz8sf0b zrH$r@VfxLvaZE&TG1?%uEC)Aj19mvCAIEKp1#?}2`qP8IGN81M*cw8F0SrK#mTue3-xh|9Q}>Xcd)(M|>iHbrjFd{eV?1@_u2##jx;a3NHOjt`kg@j~fJ(1PaHaK=;qR zhB^x@+0K+Y2e+a87;qMc`w)Ti5L3G}rtFRJeL=j#sT;7D}; z-slmB$|c1Ls!9*$>lCu?%f0mfk0G-H?^zxmc<{d~E=8Eg*EA3lfM?@)tlJ=F5&C?0 z19nZMPldplG&Uf_rffX*Q&0pvashzIYlDd+DR3Ic*HUmx08E39_=zzp+vUo_@**Sc z0Sx>%y>Fkz4PEgm*#UFYknfm)sw4OkOTTP@_c;KLd(bfx5y;<$SZU+s$e>#cpwis@ zqrEHJym^^za52F0`!qrm)2nczmsguN)P_Tj=~ueT>r*(;sn8kUQ~Iv{DfvV?!L(Dl zA&h%pWtiAHoLx7ZJ2u?8JWM<>TySEf&O-LVgCXMOdGMH z{iq$!eK2K64i~p`{JuQteykHl(5JN)DYMVj86N_cT?Q0ss9Ywyo1&93&Zif7bL-QFmMCCONMA-tt>JCdn|7T0hNLU7{FHX5=OdpY&KFl-ieR10K z@wDf=Df2(mej2ZWGN)}bNA>!hvq$;iYRab70|h|S&Dupm)1h!&1##tnWL4RdFJ zpk7dO)>|WO&_4o4?Khch>kJEJOb1B;Hr_1AN#;3;{lp=&!W$wF!;Rbq+rE|^% ztc5m04+l!Z+DH*z2$MPfqjQ49ae;xc3&fE6=g zG$!|FCVU=jAh5tS&y4@V6u!h1b6G%ZE{LN5$*cuA)S^`Jg20mnp}0kviACAJiwg6L z(oYr-N-r4%E$W*u9=f$?>at{hi*K!ft?GczW4W}JExOMm#L(QJVx2?{IY|OC;Q?<0 zL&Q@X^@tf|cK4Yi<5{{`*D8beCjoU9;CLl8AInnt&8F{|oRVndYi3tXCWI~8Bgu1a zFQ+_6L^eLI7GO)O(f({MJmvSG+&pJQ$GkWy>aNyyQV69z<+&3QoT`sY2k@4wAFQYHj zg3>eX7cC49Z`Hw=z!ITK`22V47nQ$o+-LtvM>wpe-TqGDRS*F{!TZv=$My{~ zKla*xUYAkLtNk(AQ1tQ1b*ud1{=N{-Fa zJtV-fc&_iIL;wV4s~9=p(r#{{bu?b%W1 zE;jgAm3cUFY`7UGHAVEiHOX#5%bRWUE3S?C0)SqSlQ96^S>+akkl~~{BVvmxe zR)6ozOd$D?{&PP*i2Qg_SO`^h+gsH5WFTw5`GY(cFUb3!#*QH5BIEiC!V|dFP$A^Z z{;zMd+eV2`o#N34lsbr(WWVSyQfjPg$DU@+1!-%D2mVanoW2=6gkq|g=0L7X!R#anJ0$80RAallnzn}4b+6lF|cF-cD^3We@kZ|M`#wmKtL7OuaJ3@ zyDT^aIQxf+8j?p6?4+QWKIzv%M)V8E)Z2OrMS@hm^b0DATJR*?LKH*8yPM_n3^GLP zC46amR`B(XushC%?|4;ssk6kw_*=sZjQ~l< zmQrmlCtDjnHjJT&Yrf2`6n9f&zTp4d+yIlxm)gJ_I+qawgEb}C=A(}R3)x4L-U$J^ zgZ_mejdkNC^P6pzi(Y7Y`N5#mYmJ@r#$_IyaCU5JDAzSFq>+-A9&)`*l2a}DI&vEi zFNscxWx4}KrBhTXwLqw3=GyQ0KF6f#)5E?6g2jBfw`30TQ3K{^S+_w!v&R^(2EYS zit7YrinipH-&d~ei5I|iLoRwHx-HaMnSc9=NSR_RP`J|5`lSwPzP5$ioa@5pN<8sf z%NNFW(SeYh5geqoLyZbXU|3jz^s9c3)qcmiLcj{^I?D+4E>F+Xs|ma&Qi4ye00b_3 z`@j;(coAW5|($!*6kGcxPK$S}g zkjOja!Tw6Am zBvxn(VI_Q@X+~z3dHutttn=mGuIZJC6SWd9WfWWDERb?L#q!VEhVGaVrK$FP!rZS1 z4VBnMh0v=QILXc|k`0ysb~0_HIV$!zzN^W(s*0t#XMcl9{&S&?X&cW*fjjb#KCWop zOB)MOJ6NL{6RA_JIu`D@Q#-t`u!=2o7(W5VU0}3cGq{eNh|3bVFcBVU^w@YJDd6D6 zmGCINfz!^XZ&h5{)Qd9P$$OsWQ<3WwLV)*A7HFi&$%L*Sl`&@(#2o`2LiUZgZ@%Ci z9uBpMuYqfJA^7B_<)8vDDf*W@Rb4asWPaP{$`rF9iW4;WCZZoft1mgrlowH8K@Fi` z0@tS!P$$FA%(@=J8QvqlKRXoer+2tEOG)|(8Vnc^C=Hz6@EoI|!+%3cl8J7e#D}jUiZePc9f2*0|s1gCi1`CsnPfp?f$`U z@Y=V#WxF3ELXHQ#&hfc-=kd*x!8hK%5l)Sp7o!Rfl8cW_(Hdk~Si_nx?f~|yk^mD6 zAlPx}ws0E`ZF)07gbyZ&fsQ!~L+!%LHnXxHY7((v%`m>IW2;b&*I1AO9xQo;01|#c zeMM*Ij;?HUVMgWn zeUN7k&_SG3zdn9@t%yrL1W#|y0i>G_z)cTbB`FV7)+Q8qZZC?ibq$&h>J$ZLED3#> z9n?m3lo9ujEz1b_4_eN*S7e%oD;%sHHOtv9Yi|u#jXFKzx|&$oGZ(JoYCIb8EwQ@q zNbOlmcrOy~LwtH5YW0v|oVD@$i^PsVv+u8;pMA39zF5zjwJbMiSPyaDZ_iZ%znLsy zn>H2AHmGhmFcg73n-mH_+zflp!BU!R%wj#Bf6(nZkkaD)IM%20jWT}?nLkxckP|oa zvgir&db0$Ha}4*&G+|PwRhEKr%LVbLfszyxKZbWwp}k6}4xB6i!3l`HC4dlFpJL-q znZ((N!ILr)qDe*^{H5adS9a@54r`;7CD;2+hdt-&Ondb9)8LqlKLXYpp|c_9ilM2U zUi(x;hG^x+_ovFeppZGaL3mf{{mxG(^LepqlL@7cbc-bf~>pXBpRnK=nkFr+m0DgMmJ@1u{#_?9lz4fHD9Asr|l1a>DjY z02V-UNC9^n^nmMo!c_AFKN2bmI!GwKCBOuWh!Q{#@!2p^3b=rZh1$%VCM+C-v{$@S z6dpP=pWSQ4yfjY74-!4aC&3@HPGMY5hq_SKh%OWe>|h$vPLpM;sgJ2PP6O+2mX&y- zy^)=zhGr;t@>u+nop?qrTN#@zV2>5pZX-Iyfr>=JEq3;dINoxDk2mP=!wny9zz@5C zKsHki*RIbJ!Yt+4Z@S3c%G2b@zdd3KSD)xdV#myG(N2Qig-Y$CGkLDu_MVsv+*-3c zYyIWkIihL3#6BjDM=5vGYMq`}wu68~E|jGImW3R|KcLo_o??L{n9$T&k~CYkGz+#F zhU=>U(P?mte}qq9LJy$T4QmZQ@~{tJ2Vodr8aAgb7t z4|o94qA*zuXu*x7z7Dc&f*TGK#k+eX=h)FZx+IOj*9G$>kPg_BB^8Klzm~E#>A)s% zh6=FzI+SWqzi8CCSvj^59|r@?1HN? z%<^%dOe%K<6@q3#Fq>6jSPq@0Tm$WMNE!q~>JGzmZ0(7&!M7+phsB9pAsRW2CPdxI z7M{uvpM@DR?sGBDzniT5q1u_ET~i~PYEQ$eC z@dkb~J;%14TalWbj({79=GAnG?cWro#4+K;M2hDdL?39F2@qPE;rLlHcMX5E1v2O^ zyrUMR+az^LVuY&aaI%{_=|owH%Fw4xX>ymtr74FxFPqDA4$lydGzG`{k0VUg(Yhm! zSHm3}O&uFD99s$gRE+EOotbUYNMi54m({w_$yEZKHDM`_(1Jq13cLeWtKNqIx0rN~AXUmkk=Qp|o* z=<$##D>t}{XnWL4xKhV1+snb*-BBNKDw-nJOxb2mxo{9%Z%(-lc-d-9JMB&#;qdmB z^7e7|c2}Hs)}Qva@eW9t_FtLuoSq82Hyw0y+V`<{u#gXK+}m%!+uz0~bb1Q6W zk7tT4Td*2WTn4iw)M&ulCyZ8&@u4tc=0)bnkJrb!QCCp}%i3kxzFdHZg#)HH# zIoZo@FgI|(F#K%O0V$gAnMUwQ0l?dy%N-9&TFAB321~R33`VdlW-c3_{Gm$SfAo_( z9iNS9Gj0hjc2Xf&;BvDV@MN?Mj5&KXpp2;DCyU5Qc?J*Odn%?)Jg(x0t^+*rxtXFI zpvf6Ff#{C&ug-pbJFOwbjA|I2>p{Jory7uYv*>DIKyos?VVd)*cfj}3fR@ZRHzM9N zrv%(6dUMSzplKkW{boSNr+}LrZ##D1w6DBr{S$CE`)DW6+xup3JN4h*ym|Cq<=gJS zw|9$hqk21n1J?gm4 zv%2mpo7{mL*3X`;=Y9S%m$VE#H63bHXm@<+*lKT^M-9BF9(ZLe@Y>wF){lYvcYeS7 zVG)?$Rmju`DlrOLl$tO13#y9=daXadHW0X68T77ketlp*k8Y$t3>2FX-)K++A6#i= z>cK^GQ!>d?*U;ZNQntHU_xw} zP#-2oBomg(gy%33RZQeHCg*)7*C3Po6_aO`iTcLmg)N|k7xEl8~{NPk<9fh}T$7iEz;_f)fHt{#8K30pgA$U}UuUYRp7reClcfYAyNmYU1i@Qfd_bRb*;T)bXmQWP_;0`%x*E z)-r;k&VF7y^(rbMZY@(Ynsh6QA|0I`6ipb6BL0n{qSp7%6h~!cMdur=M+9}Tfw>E! zfhBv<`G}38Z_&j$)Ux}56`Bf_RW4PxT&iKNHJ3zdZ8wTxG3N(2%3p0ORl7?2smaTGT|pIX!kmJ`**;C#&VGdc-?6?) ze9>stUq_Aj?E@7NIq9?TNSxW35FnbFGb<20m~}x5%biLfJy`v40ZW*)C43?Q{CEm| zo+6`ik`(QC+@33fKzf`d!KqCQXTp(H`%#SC^K1&@e5Homj_v= zu(4sqwBEdmiqX{KR~Kbg;x5Q3Kz3zGj0q%G6z0NS-(wJnF6f<(KBh{iT6baONABUwOt8%f$tULPQoC&07Nk0mguC2X;J zG%Sk>ccES$o8^{ea6O4G7GZ!MN9Vfv1(3C0XOxm&^5t3rBx`OW9GmM-KRn@5IAFy2 zu#Kym#m%4=)|V5*XW>)B!2XlOoD6J>)o{@YyNPymK_?R`%_NGiMMC5^zE zxpSDU0(m3Q7YuJV>t5-JIUTK?DU9KhYk&pfgf{IK*D&~NDg`7OPc|a>u|1ha35xa& zF0a|Atd_0WKEPlL5(RX=fVhv44lzBX2z+AoWhfTR!w@<9#<*2OcMacf0z7^^O{aX zcU7HeCU!zVoEXM|adL9>ewVqFo&jRe;b zKgWolqqDhnkkQxb;DaK`?jkkIE@F61sNr1tK7o)7Sl9&6!L@wdE zAql^BoWJX5>lPT=MWLrsI79&;t}Mo6+{CRxC_W**`$$lK?k5mc4A5lvS&zTW?z0~7 zpFZo7+@)PD-`V|8(1I%L60k4q)b5@e0hlZls!Q{;L=#q>pt&W|BN<7S;UxYPc8O?+ zZxjp_*qSzHq&*zmd-hMd1+!nnYOoki5EGbm@0G*8zgyND9fT?wpc>%Hd+U-x8WgW_-gy}r(cFFW^b;5R@`r+ z?PY-9`psP=x2WAjZD|ud;U*M`!63R)M;%N|P2UbodbpNQxkMCdIz1X}?M&X^y85&& z1Oi1!>rGaW!Uj1MOc#r#jqT}JAruqe8z2zAP%MP`igA&(pL%JF;!Hc}h`sW;g z#4;q1m%>BHQXl+!$p?o{l)=nmO|j!9XROO?gyoHroaOwk^4UP(2Z z5nV)U_~P$gm~ax_ZOO9&i5b&oH{&LhG$TTKOSRb!eVHfV1M2Nb-T1a6el{kbWc}`X zB7_~=$U~5T#Elh!ubA>d;eF>9$G3z5Nmh=@$77TAN94^Vzue}@xHBe7o=VC^ZGHX$ zzWd@~5<+mXt2|rzhaa2C-RvmnNBrFWpfz|t{g3`^NWmus!b&({nl^?9^S}D-S)yW_@%NjFpFM5F zq~%mUpm(2%uT*9$?MjT#)-j39eMZs<7xh5#z_zY?P{~S;SYq+JZ*@ABrnwYGkLf0a zPE`B(F=^z}gDp)*o_&5(Cbx!Z@>3t?Vu)E^qs6K1Vd$sG8S-bU%E$4b^MA?(yKpx7NAh!PvC^rC^4YyP1y0dXIu zIZTDX^sB@=_JN>+q-+Eh zYwnN+D6q|z4o2&2dC4=vb|j6%ESjJHp_?;bIj~hvhk9WkRHYDZajcw`mD-Ck(kCz) zLxkA3yr`_Ydr3W0^+=f7%i0%ruNgd6bxCS@d2##hb@M-}Ze+Eodj5M&wq|M`6)jVZ zy7!u0GS$2q)TWy~?zQ+lR`a>D$;UgkbULr8hdLKhYBpDVBZ%VzqW>Xphs*x$8^8Br z{e}X3I(zkRCZ7D@zx43cgVpIWT~R%dR{Da({O|cwPq#kBXRcic z`q44-^jqWYGrXp_zj8J7pFDV?>~AKNOZ4gfNw$G8BZTL`Y_F67KHEYvgtKI}#|cpS zLUAr!*!h_Q(V1K;IJ>mS91|VXD^4g#n7bhU%x|cBA)z>4JzPTI!H|_fVi~z^S?X7s z1~ziLtg>)f)#<^gThMmN1@(&mQmF<2pg>4~pIvz23J4%15walQEJENX8P5nlOyLzc zhF@pZAI=k!{b_|IF7{F3hdugLwq@-~gn-(y;kNp|T(rC+TZ$WMjR5$g z=cr{Eivo;QzN7=&f{WPy-F)mch%{6ptm=k9GkZBIj3iu2(KNz%9haCk1`DWIArAa% z{s`|`c}CJXgG;wwyf%4*9fvFjf&0BFQf@KnxZN8TSO1>-=K1EUDhEgj!I$V(--B@| zN3=5tI`vS+Ye#n1Z{T|o#+36xCs|Z?cD0P8blXlIj3U4(W|E^msU121m^Ov30NB^J zqh~Zv{_4n;@}?m&2{c@3oqoCbYfOe42`a#Y75QwDhejf-VB#c=2@rcn=sr+37rl8m#@{ue^S}^Pu^o|lz97>QB1}=KY{G(M;acqkxe`cGEicU8D_Ra} zrC^WbLE=);kmRcf*#gZ&04VFSeJ>TlW8NhvhVsyrLKH68Ah}R(1eqYpk*EfXt?Wet z|9M0i*>bIU;JnCRua~`00l;_egkT|KglxrXMDqk;5Y|LT1oeWda2t~&C@3vt07BsJ z%@&mnsMq6`+t^z4ht|4=xcc|MDmLb>H_~HCl zCm}FN9L_Jn9E|M|h-ap(#Rz_!ist8#lg*O^G0Iw@3d@a?AQcoJ0a=jvv*{^PFTfH! z&XmzD1zQW& z9&vPk89}2<0ZnLyF=9bE5#Yu*80>M<5vOam^T;Kd->D<}Vh)QZD~0xH2aAGa@3yO5 zD5I-V6yB)ish(rGGZ6z8G@=FE+HQ%1haeZy8|pS@2}=9e-8!h(MRt<-B}nRyi0c&2Tmo>+s)B|+QX z`S#{8npuhG*e+#IUAZjz7M5~rOZ=cEHpr4$5RCn?_ROsp!U4sGL;zDx!`o2I+dx08 zpg@0U5)Dlj!m?`%T4nu(hHIcobk2lxfcP99jOa+JYNXl4fZN&m$OUlRV4x%%5O?eM9iJUf0x1K6AlW`GD4W!1Hn5Z z5NX|9td0@JL(Rj-U!gQRp_ItA%OE2R=oUcNgz7&T!edwvDp^jreGjSuBSMeV$O)fU z>2=|DBY0G8g9TZ1OlSzFsR~i3u#_$l{e=UKWg=T}D{=?|)HZ`(0ILl^qOs%;4j^zL zbpU5nddp^bT<~HO7^+0ecIxOw@l$){&0ps!%;{ENqgx}7#5jVFw4HD7iZtjH<2r(= zhmV9SN^9$C;y-sdkLYoTnd0}kkeRZ=p-d?rKNeOz8e=Ru3x@Nj3-ac(eJsfqh-h|7 zhe1)|&<32T<&~zhvb>maD~pqGTbIB60M}tLN82Zr3WAs( z#hvpmm~+?Vvdrwy$`zn3gI)-hG?5^{(K5RiLJ-~%f_LUm&pJsaPE(QY`b18kpxl5^ zq*q`;iuIb;bXzV1(j0M$?0#uLc1K5L>k$u7E5X zL1Yh_JqxNHO9O!^*?$DTCG9bS`?L5|9Sft~8wN@Qq#U6eUqbE5Fs?p&Vu^|l=hU5^ zB%gMVbgt0?0Zf1haYY>>Z$icO{*UJG|K)K1_rLfb2l)SAmH+>${J->o|9|(a{ByhV z{reJW{?AJULx3XqEbX=1XjW&@@;}K8hO9;2AB<8Lt{vB3C}x=2jc5}To7 zS&^c@c9-AZtTZqEYnj+jyZ+*U-eKoe(^`HAJf<>;34pFN4ie?FER8#e5hHbft;?J8 zVBVg0{V^RsrK6 zEZL7W9FDh~rGu>8n;aWC=U8{6rWz#-1T(W?6ldDsfJA0G^#m^!>#sJ)TFfr@zM(dL zqxNpZOEE3&D?f65J{RynwcMoq7Hq)PNmdaR*YH%Sz&7k$|)gPqIyPVA7fS9y0OP!y|(>Ky0Bd*mYgn!`c zJ;JM+eMN}n-@{p}^p0dZ!l#_yckQ%(NvzvB2|-Ka^1)`0Yc7r6Z#=ei!n30d2q|Zl zv){Ifj$e_Y5_>IBT0xSpnG!(u8+n+b8gubcyN9p?(2=re# zSO4758vmXvc4hv5vjT_$IKN>umXymYt6;?5idp1{>g>H8o|K{Fi=pohm&y;p3Lpn{ zKhDO7k+=l;8p+>pc@$Grgj%M$+9RG-3y!Cqhi&l^N{ml!f1SDAco-_E;=^Dff)wSn z@Bv<dN49&VR%rf6vhtp4bzPJ_2^mk}BtDyyj83EjJs~F*<;C~tN$1G< zYr^tJ3B*p$@oV@%29+TDkTq$1Cl4otSZMFFk8G%T*6#Gyg0k|U%7uHp;fvpUuqiH>5PTH@hS%?PYT&kbQaEqyvUIR zr3gaB!ZFsULv;YCL}5kSy9^st+5^`-XKh_#j7|U*Ygp(rI3%(39!qcn+gYH6pn%}v zajL5|B3EnOeFs;TBOOh4*Z7abziJk!4vVc-tWEvqP*%-ev;P;a`2TW_X7A+zKL`f& zucYC>-3tHnuAD8P!IBC@54g+?w>J#2m+AlP%CC-8sCc#<8|i3#R;?X2TBpk2d-$wT zQV_%b&h_V)v1Cpu$Gc6F4fYjkfsSa)i7caE7EA{L^7IYG+pzb@dX{o;9FS#5Hy*_o_`!LfM)|bP5N|!QL$+P&ayLJ~lkLrW% za(5>ulr#xRyFNa4e`k%+;8fyZqPa5n^wjaKn~xuT-^%SN*cX{U-Fdw;gx5v(7~dtGe;!3b3 zzi2q_$%CeUBn=~ch3FuqsL+RUsy6ag?dlsF=s@}#HL91>Cc5lz>g2KpUu=~`{Mk$j`$Ypnaz ze8CU5`h}0TW&}%p6M&=fbanXfc#BWcWrqVF+h?~UPTrqu@oIKuq*{OSU(#iRyz9Cv zQ4et%57!INXRTkT`ZA#=S@%Bu*OK*Dm~UKngBsUX;Q7kY1&beXUvIl2cP=#a>|T=K z@1OMG#^2+*@aR?uk8h>Uh3h9xzYIaoipyQ|`YfT)fK6|{IWW5W%xy@c;bSvf$keJc zp_int?QRx!VrjytPh2lVyRY@2o#UgAY9k8rg7==<{F;mDf6Fo2bQc_VF=g!XXpD06 zF}dH`17B1Bq%<853rwZ`A-=m*qKazLoy(4Rw?;^NyD$220Dv7&w3ZqbZ5vN5oAqUS zym_?M=drV<-<0^sY_FQSQxwsEkXH#$Oo0DRo4w4du~Vm9@z-#^Az=R6`lZ{Kj8@x@ zYwB%OT+U2cWn-86!}n*Ngne$|ZTWS+fByt+i_8`Ln=x^qkZ3UO|E zfr~tQv)}tTgYq(hV}&@0dzNkkjGVs>^}ps{S@AiRxEXw?hkUw} zpt;9!I-%swk^<(&bvq$a2l>Vg_vK#bh`xVFrZk%3GeKG7b}@QTu|M`Sx#D=T;02S75r;D#Nf!y_ zZv;vWLJBh$W4LYr;utoLV^%T=Z&c&}H)8#mB8q4CNay_Cvp2xqnwd)YTt*ehN{^RwXek_UGmY4oL@nR7k#-TTQO4evWnbLSp7-G`@yhv#lM z&6jUKylth$*Z519C*u3#tsV5uu>)p%+pMm4HaAZ-%pO~))$R8B{Ta1V&@A`D_k#WO z?af(}cZP4i_uOu1)hBMuZ3dj`dN}>OTP=D1%@@?ghl+8WF*X)Dk?mdkV~U?e$GpB< z&UTvqyltmdXZxeqs82#k_Q|(gp@X+(QsBa`GxcK@2(VK_lHmyjCx0wr#a(i_BUWC) zHkTGxItwOOj+HFUn0j9^O5^GOG7~*^SfRjoL}w&Yqy2=1&hx$fKsL6Q@IZ2ChSRr% z=CGncKJsEY(l|-tW*GP;H}2ks*!YP*Zd!2IyRX~+ckr5R={-*4cD#4%;y#C_Ts&l{ zvUI!6X-*5ba%4tB9^aF#q<1Cvnr2L#W@S z6iFQK=Tw}D{G{JjU36nQ)Vp=8IOVR`3BM?mL`^xZ<{c;nf=qr;=OI(ygt zEFO4SLwC;U=nHmuCtLD)g6sL-ZTU+Zxi7_fF(0~GWQyo^d{Dlo%RQ1G{xrIx2${6Ei52pj`cCW^p&DHy)&#nbc3a#>WxOMJL zTe#r(wlqJTIw$)MuH~u!-fZyn{>?WF5{Z1u<_*mg?Q%%hA93xGnl>Zd{Oaa` z54GY&FR>y|wyU3K)QDDG8i;@L^}_RC-vSM#w97-|dYg>-`U&XQn*{y#KVWD6jZ>eKz#qPd)n0PH)A) zzclBht*-ru?OBWP6AqL(?M{rw_DbJg6slKzAQyRS#*4P^({WZPZM3G_(~Pm4O9 zmUbj9tW?pVmvT6%EB6v4!GmIo7|i{kpL}{X_w=iyIafv>4CO3aYzi8be z@&+~e`D~FznsVx1e(gDPSCzsj+nkAmW(j`jbkS2JSX}yWqWnz3rNPXE>Ql0Ixes)6 zF0J8e!BM>ZMMtSc7wn6v)MGL;#gpOL5r~o%NKVDw(5TsL->(Ma_JQLDF0R)?6K4*W zS$p5;k;HnQ4Cl|g43F<@I2m3^8JsK=?;_Rf=#C8NUC1pQ2(8fEHxAOdM-GW9->xoy zdOh9z2hkL1|1wuI@YFfSAD&LH&$;|K=O$L^VO;6u|9|oJ=J8Osec$+Z7PBzM*muUh z6vk3nlCkd^QiL=jiI%BU5*hohA^SR(?A4I6gvL^qq#+7PV~s4SR3iMQ^SZC|KA-z} zdS2&sU-$3%`*_XrIgZc1z0;0dOLNXjb7@R-8%=Znkwz3wKcSQEbu``kTKdv#qHkmR znbGv?G3lAJ=@)e}{EudAv1OcYycO7ZEx98D%CC82;RsnNB|;}N>RL$jXhvjgWj_ z=gC~%*>$l#$g zA6I^w=B{QHyfKeCD-Z%0XC%L-yr?LEesKDUj9!tYoE4^u$>*_cP!GPOT6*7r{)j|1 zh0CcXE$d9^3s^wNoYr;BhRXTIOYQC}5(F$^!@ zF4i3rGWuRL5SynbQqpD~Xyj627Fc4DU1HT#Vl!4^w^2e6x$U5P+tKCrk-*!|*|%Mk zi?58{cHg*7tUTr#=;+~6>K#~mOt*Bv2r07TeGR%B*ZWH(jhj#cDu zR8U1Ki*zeXTq;WgE6cMhE1N2-$13k`RMJGMYIUpXT&f-hRyAZ-H8oW=$6@y_WDnGq zY=X7I$x?spW4sB?l59= zHeZFk&OWkv`%a(JrDxN3-m%~9)Xo=rS`_&HBE6mxyzGK8LSDb^l+BLw zcDhN5oK7f@5VJh-9#-F`SpUhmp542i5K+%pRv*+^PuN%OZB0gIJ`{ME;JaD3U!eeB z6n9%TF>&#Hl3rtqYvYrz4SO>iqaM|TSE(Hp)$pkk-f|35$-ciZV_V?5N6qPANF%=L zssYE>``slzZ)F<_tfgK;_cpp}JbGl+^2VV_w7J=@pm(h%?|O5C*=a~zbDyh2uS@N7 z(Uw8KoC6u?oPuqZ6-mTX8V({XKIHK9V~PK@Mev|&zpK@I*zzK zan23hzdyn>q2t(i$ML==7E4b&QBP0WJh4uo<(L+*C0y=3 z%e%VkYHrt!=B|+OF7p2Fursvna{4Rl(4d{4QA6EPuDLf)p?ZRL_k`N?M4ah~Oz6q} z*`4dwrEAmW7kyeKpxp%lRrMRz`Hr`va|5@Cp-n^=2!t>8Vq0rVpG{9aQ>c12hV1Lz-ylC^xe?Pu&H%Brvt4+SNCSK6YV3(=k^#?ns zx_zRI?fnc`%l)-y_t_E|Q1Sb4l{&*+FTPYKEhthwB44moyg2fnmE?Qqy!pUo@xh73 zcZdn`@^sE7Bwue#^&3vl33a+NeDrKrrd~TSFMelq*fVk1 z`}we+LLo0_*6m$QA36vLJ%`H|A2FiZrd%)6^_l~1UtiCA9kliOhWJRsu4c0SNbuQ_ z$QIiOl@aN~groJZD9_1v&pbJATW{#%~-G6yTrF| zR3_d(f4giu(WU=x?by3-!S7}(TGyBRo;SaHntkg*b?@r#(LE}6xAPo;{OiITqz&7< zY&S^oB$X(IOR2VfQcWt-<4lgeB$Xdb?iS7Ir)t_+9y)f1BYryY^F6I|UBrh6KJ*Oz@D<6uF1s8@HA)({ zU#=BVskO+T33aZ*Y4ob1ob0sYcGkKW7e@V#%-xfi!fFWjyuGCx7&E+1}T z*S90J+0oa_xEH#{-d)?XU^LQ~)xU6UVY1cxLrMOJ%GM9n6Cdtwf1pV$)*e`_+a4VZ zUTnx;9NkTCnpkYzUVJ=1evbX!^x27ym5DCZcTf5!p52|8eZKTDap_gdM8Cwlrw5i_ zxGz7qTOM~`oY-DwN~}yBSebEOnY*zvpTF{;E^&+q0P|HR%fDRba6`;7rH#XB(GbC}+9o~N%kA^Si3WzX3x-co-t zbL&`r|EVv6H^2B?@P#kkQ)?BwFdk{57MlHf+#)2u2Q+KDb4dTl@DG%y?ztDC`Hq~T z(}%eZ9Zkp;%spwh{IG85xsTAdBlqU?3S0|bS%1zX*vNlA^FjbSrq;h6YjD@pf9HU4 z$oGDZB_$ho65D#H+IqpncOFzESN-y`k5b&@yv}-~+N#Vimzbg9mq6xz+zS#pnP0 zrT_n`VE_8X{Q6h_sGHsSdv&uDgj(OUx?A^teuW^pL2M{d1~vH``}3+`tmcMiOlU_u z=RNmf`X~EYyCkTRV<20YVrQDlah@3(UuQy*$S&|5t&WNTiiNsGd7z1cV~8rpx#hUa zr%aK755*cchRRuf0<}5dll`f&5>c?H&$Ii}%qAkpQL7sSj(&QR-&#WZsCsnIU)iUQ zMFGzrg_0%!^s9Y+f=PlzMNW)Ef0%_R#8T{*$OF8xgT^wnLr6OkS;vY;A?yRd`$7xfhZcbRy@fD;qdf5FPdYnTH9|~GA<>Wp z`Ys?Je-iw!JS={9RY^tak%f0pGq!EO z_)E~|RXIMNTgHWW+hG9TUcAm4yKPtuPVYu1S;p2Lp=bV$FJ_vJugr&PC8xs=L1?^%Lj#xf1g7V?sZ2uA)v}jVvXiAz)-y>1?p@gPDSQu zLBaSSCC!V5zlx$!eFQA*MIMMG@V6fJUl-cne&Uxj|Lvk7G#;zu?KM*K?kQBzs8US( zqLle9d3z1U+Q*X*!3^EFO2cW9$HAi!xXOil#~+{3F>BRY-~zEBQ4-}uCwLS`?prs?CUVf4qB z_eo;E1s>N1v?Y9uuG$i!gcS%fPZ=^+0`FY|8}cMxYj{7`zVhAbb4M3aEDnNeZ~qSO zM0La{m6wq}&3dKQo6o>#*FQ#aE~(?GxOR~;k^PDp&JtheZ@n<6dA>L6Q2nuJ<13v) zGEk&G1s|qxR596HFJKiz^i}zJ8;r4(4UOcyuYUOL@lQkQ5CjTuoN6$N=eE(`=?srY z3ldrV^sJ`(LAF~hYy~I9@IdG_)b3Z#VATi$9>g`wG%`Z*VZtaXji-3RfwkemOqrUY z+pfxYBQQp%?5;SR^sUr#NC`G1D8MhwogTvnL@5#Qz~9gbzh#8q9t^NJ;KOm@&G`13p|Cj2?Jm?NX>JBjiR>r+cfDrI#EQ{r%cX{7kS z()|xR*Y=wZ3tFLZ?3VPmaKeBzG;w?5>3;17Z$*)mK_@F;Z4XlHWazDkTO3=wM_$? zZhVgDFE*-jKgk~29^?1*TjX=|n=^Jd`LxR^$t7n~BxS?Xe_HVUdexPS#BNLnq4oL? z*k4}tw-n5=DEJ2xxBfk6?Y}lTU6}*LTvZOY48d;2c2e6-kjCrdLr8ck`S6+in11B;I@wXR6i04=96gn`8^W%?0zOAHQ z?nwrRgOLbFT9&4#RnQr4GM%1$vga16%ZKojCi^Z94I;6m_0lPTYRG{S&xS0J|DxI! zStRlP;k{L}+FUyEG%SEZ@+e1ONkqdi%=V8Qi%(!Mv`yW+CyVz>aU7;VK6LL4HP$ss5bK>@JxYNp zoHN3kEIT_?;CX6uebYYh$O8!5f^&JkJafYAxf7uy^prcVF?{k7WS~gZHbhdrGot%} zN_U!93tIk{|Jem-Yvl$}YG?mSz;7Sxw+Cx1l7;>tnKfkbkA7Dcwi+IwPzyUEVKNdX zh{=M-@j_~NiUlQ*s8n-$Oq=_w^4GxO#*Iu364>Bjm(+dh6>HhHH|7 zX(h2IcopWJ1P51Q1U^c(Sj$Y+I4UX1yWUY$V9k19P1ulpMiE3hHhlTHqPklHMtzd- z^uTG|XabK;`y6lHB^Ed6!QV4k;3hlye@l7)_A37e4_G0^f2h)&zpF~u)r&iCh;B(y z2^v#9v78{G$xv=%ix3(86H*SEUcG5(*R`OY0S}o%?D07$$zQR*V0VuE6XeUuLX4g1 z#-cM8`l>=v4!q2`yKN0 znAyDbMeZQr{`5yVo|$4dy5_OzgcCo_F5K#I1oEy2~b#vswQV@5z3) z1U*ZK?!oGb#}}RbKcwv{#D_xpmFE+yD4blkL|Eo{z63UvcS=3=%&+U}6%$ZR-A%YZ z*Ov_e{|z6Ci$$dHe~9BBaTV1NV0DiELmZ);R$`eLR_BzErn|HDg%d^qM&nz@2p703 z3BD_3?e+PRo)|@gC@-CqS?g3K{<~sFFBYQruB;McQqajzx$3d4;@2^f2h+FRzg2ix z924+vIbcDC+1c`FI^}-$N7g(6c`SDxlWH%@&ldv}`_3STXCC`{F97Q&Bf15zI$b;i zG^Wfw;}mraxMcdmAxh!vSRFw7r1YXg%pVkv>5<@Pdo{5fnIp|m?C)6n9Hx; zUGc}BkH=f>?Ft_TA;HiH_82MFn0GNgG)j7YFfov)tS@hGPM4$QeN~f8HiH{8tnTxx z=rLXgIO?8wQ2Sq%oA{qX@yl!aTQTQ<6$-IRDVfpMfHDm#U(bLHpW=kS++_H9E~}9J zPA9bg2?+e5{^PtwG#mz6{r*vs?7sO;%GUCtk;EkdQf>nvHPe{}Q2a69<2loHWXIjv zu)PQ#->=&(9Xu)3T_UPNI1852fz|U&p74*YyV%#m#_`>Mga=@CTpESX*#u%wM~i9y z)oc1KzyJ0??oUDZU4QQX<~{xwLC_;Sdwox=0QJ*7YR!DGqtU{^XqF`ivlfwZQ*ypi zf*T@Y|(ER?s?{jipqd>a&v#LouaSqBjJVIl~sz#tTHSC=Ze{A>asl6gg z7RD-KYrdVAWR!`h0o}jko1H(`JNPuW*Yk9gX!rx2hedw`*01{Rf5E@luM_ZBYJU4@ z8h4`?b~_GP^9*@1wu?DMfj@L>BF&l$dn&@`F)<{3GNNj zc`qCNI@g9RjE?WfliO!+RGW~q@B|5~UmF&Ea&LAbPr=(JlR98J*+Nv&4J!X$J=3Gj z`SFZ)7Ual_qr!1du`WcP(7$SZ-9MkM;lJepvh(=3ZJz3PfZf9Gai>x{U8`l2SGUxU zYIs_~{8xSiT!;{=y0IA(1tk(0kGRy_8M+VEeESnopJ}c&p~jj{+Ob6?%A3=k!@( zCOd@pmqztObI5o)YwKtL-N}E;4*iyo|Mdg&KRN%u?aM&_>C5z13v0|;0kJNtEMG=S z_77i%fvB*|J#cidcpPtOhp|+7+}i6Jd`z`vq1DgPLf4kSO~#;cV_H=Q#DWZ18hwYq zNp-uN{{P08Irt}sJopWV{NYoJSuF|HNJ1mU=frzsKt|>vTiQ3E45nL=fQbD?lg8SU zGMRD*FRqo-+H`|{j<8*9JFj3Bvgd{A26vo9T9L--Z~LzvdeUUD=^!!N^I1%mTZQf9 z+c^IZ&uB*hsNArZSRpkDQW!ghq0MWnA)-=#64pHun~HVrwybmhahPMLM;@fCIK zHY#Qed zPPA(8zaAyVf|lLqf(K9Xrt-;Ju50fNC*G3^-ioUD}mS-l&WJ zG=&%`@vH&YO076$Ypr=rZ6TS27=>5cTf1I+%XQyo`y+7kh(vixzHUZxPnlFnsa;WB z|8c>30P`!1FWlo7O;dXs^&E8N@6i6QtFGUE;xy|T|B)zQ?QQy9q9Bw520c3z0+(^fEXY3EZqMC26nYCYB?Ja~H`9|!=pq7J=( zmMl1}$f?U`G?2ILHF)TYrq7{dv%*rA)1o*)Q@6hI^J2dxy2?YL_e>W*oa1WxxCv;} zVNMUgdfPO7y*HtF!{@#c7NEAHFud{@EEcSOqy{bd0TsU{WzwpeEU}gl3W1_ab+p{x zVjzCQod2FxxcH}fp#MfaP(b1A{Cm$(Mz~IdaX^Lf)859Ook`*bQDQ&BI?Q6WbXmJ4 zKqV3J!pApmE!-@O*thZXZFr&Q)1EwiHH%CvIYGZj&91k^auvucQ%h)1=*E5=a! zE-;f>Tj+)Ix&@}4p|GwY-|)PIK4r}tUGnc;!*BT|kaa(Te}r}0f3C8?@F+TFVgSQ1 zIM+sw#egIDzYhJ|xw_n}oHe_)n#~dW2bvkfehxLzP&mlviIXF1_fA;a;m;edMB08)i`O1Y?(q9oTKRT6?X|%<(llHCEE(TyB z?vI2R^cQsA`qZ*yQ}Eeu%4v8h69fy_Z$4nEFrx6f(l@(^tmJlt-^O;DpQFtExd}dY zXgP&ymY8IvZj_58v#xjK-uX^^o^EvbU!BLOKQ&Rze_z#Bc*4w*HC3i)NZwP=B7J?C z4LY?x=7=Q#A*cMM3@>JUUF5QemTz~3Iom5Q-=SH%yE+Oj&0|`NBdG_T=RcvdBJ_u1 zLcRJr`&2vrYVLmfkpKEY5{u5imtgb36&fXMseTKmCCeDz#F(&(5h<*}DdeHMsK!B7 zM6Ya|6hS20?GJuFa$65#WZx39tUb#tyL8(i1qM3`X-_(+>a9lWR^(PH5G~bpHPXh# z4g*5rE7)bv(Nx)^=hWyGN4*lweL zM_h1RdKN2bI#_p_=hUZc4!GIcd@}3lU+ECcV}h<5EhGObSvh~c{n&4(H(w_u}$2)|AxsXU(bI0ZC*f6niO0KmnrUB~=gBwvZe~Tlze!h3FbXT8fm%zOGT-Gsx08-@ccb&Ze;rTPplhFWKFKKR$RTPPx!JzUpS!6n%2(vICvYnbJ3Z#LHgfV zt5nM(?jN@3Ki4Y#@3si6v_AnsJ@Ggesfw7vpYSw^lQf9x#24~bVD`W+n*3m8UJ}Ke zJuY(dLb{T8{Wh!tyy?CRQ-f*u`HWz9NUwIETz#!}yO#r{+rS>lG)72u+1!cgShg9i zVWuea1ZFM5qo}kpOEq$CJeO#MZ5-xw+bdZi>`5fxJz_9CfePgW zDIfP75lMku;kr5Z*=KShkK+bp&3|=7yzU=(|Dr%l8V341Wv<@}?{5ztvo83b8vob) zJPAu?&Ckm^HxMSB@63H2>KRYZ8qtaB`~s>j&=Hv3=Jk2c|ku2TEh53UZn5*@$xPudY_?SavFTyN@YFrtA+4V~<)(;)oZWJy28Ji&frgIz zR4-}Lh!N*&sn~1O-n&O%1%->-D{qzhY!_%m=t5PF_PH|mTqe~-sDVVTu7qBb3Wu_z zG|Ad55M&UTR8@4?n)cZ&sXW?S7TF^Iae-S0%-SFl{(WUk-G@!`vH0H#OMZKozdd-) zy5xVf4($IUv?l$ptpn!*5Zv@wUM`grUP3h!N}cob0tZl?TR6a%$h$Yygc+*{zIQYmOJ-uVCs19ucfN2^+CdK>>jsnZLWWbH@Oua z-uAHuAay{mWqN|pfEx(}=L7KVGAr{^6kG?u$h#rrT|OBlHZ}({;>(=k1SGFu`_;hD zC*Mh*Uc*J|#m}hFyQ077(AYhUsgU8%EpIfAl>3vIQC@N zhua}Sv2yc;G4hr3MG1R9__%P&2Y_7o+!XoQ_{g{l)3gI07HslvC@UL_^%X{hd0(KK za3$Nxg;ro4cS?M@ieFU@RzG~`PX>^(1VcpT5mv+~I)elw*!qqdhF$z-RTlPKp4rX` z=6vag?8UaGk1v4VaUfv#NS&HS$krzT{jKozQs=N z+lv$PV?brfi(zQQLoYng@~v0JOsl%%Dfg0CF3p7dQQLXrZSd|@Joo)iaJTPtY6ps! zLQ2?U1PvuX-s1*{vuDfXZYy=>5e(dVzoVIKYy^l`Izpb z-E7QP!iz@ll%JJZio=5*SRB0-zih-;-dFpK>+)8ODfHvGrYW}q$X5C7+|1etO0GrA zEKqaj?Z8_h{hA~L8?aHR^yd*;8?1f&vysSA@8SrB3r~g*?&ZP1JUCkh1tsp%Ot6hU z@WNj&RDS-aCl^)>zy0FC=o(Sg5QR-fvolb&5H9!yy2XcCu@g>)a4{-OZ(qmvd#@qa zX`1b->S5<+-}uWd%DiR*k!`x`KQZtT(d2Mr`0mGh*I)Uo0m9!he}dU~=i5J{)e3HP z_aXU1f2}!&y?u233x!vLHN1Z!4v~gg$B^0}^ zHlURt1?5>ILqtbArpQSWUF|c_5{6)M zuXmg9{anxAp-hT8bsN4i!G>{?$+k$sD`*n!zHlD^M&fQ87RlFPf~tTu7v9}SY#6&g zi7bn2(-HB2@|+>3ji&&^M5VetmQ-I&0Mu697Av?$6!T@!@A+c(y7p56)GqHGGkRaX zI`(L5CB2^2B3C(3d&|jHUV%h~Kp3E#nlU0^J2GVb@UC>6p9#kd|4!J|qdd2Cs7VhI z3{X&~VKnw8dUisujpxAvZaq&g?nA%tdGWXkX6qlgKC6jPal4C zNU`HVsJjX}4T~$p6DoJxCO-3JV=b_2zg8A=1Pj?i0+}O81LwcpvXxqWgRew}2?n)^ zI+4QElyK2nUWL5V4ed|)+R6JE({LB6nKpr-I%dg)+C1Z}^VJPMzmjSGKyt;7}G zL*bmHXvw>oPXwJyA)kCq4r$!Bd4F^X=v*~Dw7V0ebLAx1N~lX@MJh&+=phz`G2|2} zAz$lu!e+<`DQHomrL|J9oS9v68n}vQ>@z8wf&6SAFGPA?21Klz%;kYIG~snAi6Dzo z!zJ9!kom~$ON7Bi7D|r-Cf4%I>Z&2}ia}^|o9OIl6vqAbbnxm3q@IC2fCh!}+Tb~s zecCyanCJ^j#u8<8^%Tg$#Jh>$?60Nx3s}FFg9j=i9{O z(P6x0nc>)OZHn5WpzS3Q_{+O{5&8Njfxg1&aqV`LE)|J@hH-w7K)hr%1(?=dpH%Q! zKd?aqz5XO4b|y1aASPnvqv~OfPXs8uh@6D?d2?|~`zvp&__@ldW_W};>Rzx)@~(v@ z$>u2X>)X&PEZn9gpMtiDMbJ5Mipa;Zy)n>{=j(!G?C`#CFECF(K}1Pm;4rAamISw_v5Dir ze*#NqUj#C#UT3G-k7C(Zs4yM|XDa})r@&ZH#!EzmE0*I2k!?R20$)XpGbEW`{sa0N zr^)WfKz^mdRd6@H6K;S#!mRcxt!Vj_*0CC1VABAb4r8yjBF7pAB97y_OoD$WoPGYy zBeM>IVuI$VVeusY{5kQlu3)77-Zngk5fe(%3i*09fo+z0BZxK>}wfhFMX8cQlkI z9buaZxrvW3TQ}2Z1llp7=SMh!as&hc&ZnX^=%^NkzKLdtK0f{&1rkm|m(bzkwc3?T zu1zZJ(XD;w$wAeE96B`bL*?upR8Rl~N@8#tk=e}W6CTFIn+M!js7E{Ep#thEV)torGP>3x{we`-M>`BoO9n3x*mt;%H7lS1Jz*sv-c;!3 z1U=>;fh(8@nPPApWg_)rAgFH8zzDQ&6}43pU&n|)9}v@C4nqOq;2w||Ha2zMy0!@D zq?;X@z4di90iPAmiA(h)qy}T5TMcPTb*3#A*ss{&p{piKBME+45VB85NEC#iCAf}- z#gzqCn{(KnM0{XK#IYad;eM^cNzl zG61!tK<$YTg=mahre(VG>31}>Ll*D^;gpS3_x6|QIywv78T!c_!`Tjq6LV#$$Z-Io zK(+B_!I$IXmN9S{GPFiG$KBb0%L3fF!$bwr^#nKiw0MefV*XU>g5>%hL_p$}1UYNlCNxYg0S$mlK{3!f| zaNbKD(D1dq5j1D;bAiP$6%N%7Ow*r+9a9X1l9j1bEp~e#n9s zhT*})yb)nIidyhf$*Z%tJmEt*r5C}#LYY)pJdv}zu`q7#7hJmPrTiWm=Q-xJ8tKZ% zM=Q&+D~+=Au2vSY0NS0KGy`y4PFV2v3DlX@+uDS%yMepQYcU6X+3d5ioDCJoY)FlC zX;w*DxfPcHS>!pEid4dYsT`k(Xmb~(HX^$j9THlqs<{egp|*=ND_>p%VX1r_j1qxQ z{BKjruKSiQj@|XlhOCakceAI!-{^14fn-**Yjkp6A^|Z5FpWb_LkuK{xo;t+&LsE; z1~hu7BXh-+1JcXVEAe*o1}~<=J8>vS44XY3wDp7^JOI2SBM?{!cP;#L9Il4Saklq< z0uvoV01ec-LAi^z>0Ge{$Z<*y>QnHVe5JEc%6EunG!{C|L{F|lOb8I@A)!qgTyzzD z1we1$gIUX2b1C_1G|<#1v?L)Ma1`T|QBegRo>)8>=;8%X!S#ecu-NGUV_{EdR8Xmj zB0y0W`v4jbht!c!d&q1VZ#XaGAa6X?Yp6~8@Q|+r*v~L3dsCfycu5VFQ=0lPVlQOd zu<2G&HD7Pk=9B^K{z?8rQJWaFBNp80VAoV}n1#k3Q`Mk&6hqN_cycGl(!jIWoZC2f zvwjoP5S{~yyxtJ22L`kx$o3L1NRZ<-$FIgGUS|l(2sQzLrp6_p2BW8UA7rcyDj&iL zGT~d+%>WMZjtDiu(&^7C)}m^eZICHLi5)uE3p!}kOjnqs@y-=8HNg3kiQWK^DtaIb z3|9b7%VyIUq6rRo^td(0^5bi71SiTn>gXL%DG4hWnnXYhQK{)>;_2~?*^fZ>jHb(s z(t~{uRj^MS&pcUu({iZ1CAJFs0Rs;Q&|l=~eLHk|&wDxxG~A`{iF@DS;nD8x3++nH z?E;S;aW&`YF^HEKUFGVJ-dpW2{=#9#0Q>p!f%PP|2oe$~aJ4Jf-S{r+n&Sxjc49NU zkXO=%yIV20GsosUX8(C=5K>I+ndYkKKC;3wR}dQw!6bKX&}-jwG^}U@I#VH!@f>1| z!~p&pIx41tdzTbKlSQl^l>G!{J0n4@WiC5TW}BO7DZ%gQ;BB zM!_wFoYhZITL#pQ0FkGm)2ZjRZXFhtsr=69E(kkL?~bwgAY+a{Ln zk-4H6`Me3G|Ai)`hg4ku709J;yZr|)0oSl2TH;*30NhNsZHU3)MtOryP-45mM4rRI z$wOAl^a1_1!*@D;lriqu0d^WXm;l#E6zQhcv3Y7c6G# zfGW_A9b=yyM=nDl;EBO&V065J>4A3^{XN!q58H0fzuD9H@d-kO=WZHtbV-}!bC@6 zV4hUq6XW4~BGQPaDN9Ga#UIheHmNS5qX^&w6f~IoX?UFtly1Ze|F4?+*)V4lFJB6gGy z$lJOLU1(i_4MXNvIZT<5Qw)6q7upn94g#msZNWPdaCO|OIPh8e;OG4mR1XDeOXj*t zf_l;xzm!462;aeyY;t6DJpfInaD@;cj}vR13cltPavWZToCP2-(wrF)@gC0xI;muO z4mA9B!3zt)(^tfb! z`hCr!x$D87Uq~#!Z0&kv!nR0f_X@ckPiGU@A+pJav`r*$o~LcL?4TcqJPoID_Sb*} z-648}R80ezE)HQz0AB?}WPwit*iUj?TL8CrLRdhoi!48V=BxQPd-lIgp#X8)M3ryafO4z3aFZMI@ zb|+jw%Sr@?*)y=DVZ|=p(qbg5;N^M}3 zbbjuDDHGdPJ{?l|X}ao!`u<=rztxZ;P+}E|yPSRHtSd&%F?o-ltX2AR*TBsAADW5+ zSEH=fE#T#_B&q3g3^(a{28YJP&c>`>nqe$0XnQJIBgHcee#KW-0?PuCGG*r@e+^$z z%h7b&cTS8N{-O(Vcx}68dM3R7JX0t8&^Uk%ewWRwOT5^1(7jY_iNABK{@@TA$!pX2 z=2R-YKXitA3C&$3zDIFhD)%^Pq^bxhi<`*_*|vO-LTFFQhYRAnKX9C%c~izIPND+j zFRDZ?sc?BADWTxCo~{g(EF(fx$ZmwH!+)ZuOfpDy6q|S@n}RSqG5*P1!dgQJl+Cel zfeo(sl)Fqa(4mJ6k;$MS05o3?k_>xgc}g-+8mU^f1K$0)8wcu+69`@vexeL=oHTzd z!xuD(xV#!brH0?VCcguRO;8n5MFObvtv^jFLScSuKi=o5wx8gt`aYF&ueH^tq4c1= zQTb{+YTpny6r+Gi?$iXji?RYYl;9i^LiqOV8{ZmD)Up+sOcv}_b0#(bm^&&VW?k14+xiyCU)lD zSTl?+@$}YuZz)&arj@H3P+s^lPKaS77V_C6ik%zpe6y%Jw++f#kYH#SW;ioi|E@4j z`K2jD^mLmoz$UxuHP$@Pl^fhY4-&bt-P6S{ZE`r%BU&mwbLu*Lv@~oDR#O@!`c3OF zLilu@dau%Df&QJxY4F{cLNoVQ=J`AaODw@?AB#C7+ece_-6R5V0;A|NPe;?R0V_l1 zXOn~D^C&w)*VF+TZ?xo!E*4{0eO8?Mb|oWHnJDzn+8oGFXD-D z4D(x{BTGAK-w!!|bkutMBk`bV%VPfb%zFK=H7|m{#T0DGTC~59MdHvA#N(-Dw4ds* z2;CAo*Pao59wV+0{?qQrT_;Rha}~l~$`o=vel3W!phqT?kDqM#Zv0IsIksbL60vo{ zi!X0veHryI^}MtN9?hbk~7t@a!4OcO>OSJ{gYKBh+$qS z%PnZOOYT@|voO4rBKLMZi~?4&Nw14IiHo#scl_X)cBdp$TB+xm0P1DdiwJKm6%DZ8 zy0Ij4$EUMXNYVCORATni&Qe&DJqwAvICq^GF8=7K6_@k0(X6&}`zxInOAo%}eJVM1 z;A`Uxd*wp~eb){7(v&O@fl~|Mc?AaCO@yeujyC8tc|IK~wBOO6Llz@za=P6-Q6jZi zob9xQsjfpx`cR32>1iv=!2B=G4!6}J7nFplV8sYxJ}jVv|BT2>`@LlVh&qkDdxDq! zXKE4~1uyo#?qC7<0oaIB39=s>k$2_34irmA$``#*K5-a&cU=CUrS8k@D{GZU-&QL2 zxC|?L--e>gr^Fsual>$A)=qCf_pcxN(0$XGGily%^h{WbSu)rPVW$7B4W*ciC3hHA z@0`f`6cw^&ERo2sh_t&l(y1zNf8_d-u0GR=ZuUiuJ5N0hdGS+vc z?q0}A#j^)<#)gwVKfHbCB-dwkc<;Axae|OCgxba_%6ve35qXZ!){M8Js9o$Etqq08 z9wuqaAogKyzseSVySV&O{4Bvp!~y^xqlLklw#;j3xM;yOGHRM^gw`0#fFL@A?(V*Q zZJ=s`7Da3MxIE|o+OnGHTwL1up;LFe5N+-AvQC`Zp?_dCtZ;AL4S>Ql_nw&WXp%R=ce1sTe((x?D0FN881t8DvoS;RxhArmzR7qWN8|+ z$A|oIJ@lrrB2i*}1g;0ZP~@Cx1mPzcA~rk`r>O11X4ng+{m@66az=*oU&OyqNlw!%3YM6a)7U!(A~p2BIQ;?Dp` zhyw(eqb<7cWW(;~1i<81VWhQK(aN`S*(uMFw~sZ4uv5(_y=`#8^+(c8b6*eqq@I({ zD&`iZRvV0|do@|%L74&OXm{eGo$aIu_+6KT5MkGiPU8hV93Z~L9V*;MrdZVf9L(AF z|CLcS*9LXU*?n?6r8aN;@dvblkq|T!zPz9G2t%Rhq}|+UU*S^^)$1dK!>OKp(p0Lz z>ZkDjFDKVLGU%cWD}F?doROzy4{JAV7m5{*!OxK35&$OBA8Q1*BAV)*+x8od4xYUD z-nh#nB$m#VkHBU8xFs;Liib{q_jZ8?32E-NzL-3(b|gI88Tt>*HwfV zq5mIkZyFZkAO3$|%d9Ok(?0DpEt*!-KJC}Er(_}(!bBw$(?(jjW~L35DIvsnB7`sz zvd^?fQbZw43qlx#5Z&{;fA@p?(f`SP+)w71N3(pc&vhN2<2>Kz>!qIpB+BmyoNIRw zN|+5uJj^b10v1y6tT?_64>G3VHa7?@QeaaBnkg^TR;op%5QA5eK8(*7VfB>K`xe!W zhh5TE;5V%(@udjTnS~YvY`$1PO)d19DfVaaZ5=T*0(RyLpj3|1N()CX79}+Bm4`W} z7Mx{iN$3pUz(_bp8$OZA32+PC-B7d3k9mbvUvepQx6*x@KMXf4H`8pvEs^kbxUilA z*bMBPKLQ(304}wd&<`{jf-A*EWFR+mR^IS+C$_0N!ziZ-YY3{aD}W zHjPPKtOPJ*!kA;k!tNqd%Fe)kKD(K3%4)~el=#QBRf?<*QDvImY{n#LndpC}!Pb>q zLf`AUbohWD&$4sx(GaDaQNd@T{J_~Fi{XyI7JlyyeDGmVI2g2QI*6MkBsFx-ZFCXp zvjjdGMz$U8;vr^eVXyMCm}GZD$&*E75s(7lT|*(~ zoZyP-HfmX0eoA0v^%V^coSxzwWfA06i}y}#b8io5sO_%H5)K>-XUyQ11A-DBVuW5M zR0lap@GALW;p9CZZ@84(M5&G_@I;N+m(*E+y|`pd-zM29#E%K*mI}=|h4~zqID?Jv z1|vd?KRYR@8pjQQD`wuM+Yo=m1IpzHZ@<*a&Zt00b0BVial7QJ@=DX(&q! z#W{DQlm^f#4nJOBJm-XRfhG+D$6W+q4kI}@TRgi4ol^9f({M5Ud@bo2d%uDkeC4aKqK-uL2aQWK><2s+4(OysX^L^{M3AyJ zjCnCIZHG0F3vs#?>GHRqvXJcUI#Aoow@h1X)WCm}GXF%vo<9rVyf{ok98RNKsh(;L zObYQm2a_bdWJlosM8qyd?>5xZUf$jqBF&a{$3U-EM2TY0p>LM z;~Gw8q5YrKs&P7tgp+PokFLyH0=`cLVdOE1`XE~YBQ1DsQPhk4%bRK5Pr@Qd-+Ij_ zB2B4$4cbuipS6|>V|0hB-R)eWY6sLZBScFED2D0LliixVQ}wboQA}4 zwkdGoUqi0w;Vwo5OfB3x%sgsYOzBoOJ@8xm@hZ2TMIKyMatoyP)D}z%`@DejD=XCF z;2p#G?UN-t8j5wPnC9}5f(F=J%5N-RVY(X@ETyP)SLT9zn2M=8*SM+$uxu&H&nfX0 zHEpn`?7ffEn9;~jS#HGCA2<=SOA?!p)!1`uvC-sH;}(!mf1VVo+~exEbDo#%TQPV0 z+zJXuO>qqH=r5TS?U!~`-2ZU%*2m{})JJ5GOIptfwMqr68cO<-(7XHg-zyJ$nHKlT zn1AiuiU(~gzVlYy`CF2d0-nrX?znz_>hr;LsX&E+CUYu?-9m+-|7;Vyc?SR95+p4X z_uMCG%VQ6JbUxGqynRtJkp@mpmaJw%W;4*%(BWxu{5{KO|1EuS#kJoJUeQzlUi`Z( zIX57b0)8>TY;GvYXU1;|I(WDob7bGZ7-J|728u_Rs?$S3YV7apRdJ?798CzxRu|r;LMC03q!VZ&s)&4_jG#j(6$F*~W-gr0Dn=Oiq*6?ck3QipLR6Ldm6#9zss?z<*IZwYt zPxvw)T>HA)KT>GI(U_hF7K-uKQsK51fmbAMbvJ*$Er--!6vyKmN=#uS=Fp5FlLrO$ zz+2+@AzQE(H6_vA{CeczzxPE<)f0#0sK0Mwsw{Egzr%5F057id#0`{)2(~Z_&831p zK9x(i@WYov*)$qUq4IS6F9U>kCSR@rnmL%QipTExH1Csokv|fxC(p)c0*TyL z)*VCYDXyfFuKZgiTwLC@Qzz zxII5d(#lzQ8ELPQ(Z0r<_(ZFSsl-Ols^Y&X|LnoFV-?^?L`P)-KM}dnKmso8R z9Z=#c6}Xf_*i9HZ zuXwFg@UR)0E;C=uD>iC4Fda(w?!(zIK0L1-pL@}~kUCSeNeVTpRYGG~* zY%J9@oWyQt32*OOR;~Y^_F^s~0n;ao>=lLi6!`tWSbC|Gn^fR|H~}c|Z4?cXLu76> zYZBNThkJbnL0=hhWI1jdnoBZ@|lkM=WXbPm2auDVeZ6yfp z6q_m6KYfZi`u(K|fS2GD#p`%NMvYJd5xi*^&^v{Xhc~-*3y3`2x?zC}<-TDWpR8Y@ z#3usF3nPvyZy(Ic_d`))K}gGIqsjCc-TSw{7CBKdwAC-}^k`(_QcN?8>{|+Rd9FfR zo_n|8M;~NSgUv-W@AoO@REK$H23l48$CS;m=4$U^$9T74h@zLS>7K3~n{M2VF%5BN zP?R_7=4st}=SAN>R`OJ?^1MvHcnqT^yv>$-gnfpp?o^>2#TC6*>?AFmb%7E~v2>=u zgi~MTP^f(;ju5xBUpMWcTiRenMzB?S>HwCDqM;}qD=FSOEWD2>5YsFYvuoat3}NWB zN>>&j<~xKyWcjAgxXoYQd`OE(L@1E~NOsx2#E+zD7iN{c6%T$TQ01alVtinz&|dn! zF>2z}ry{#J%%hQ&eJ8grS%VsRT-?P06G}@C-zd~5ExGgF){nqnMdU{HgOhbYw73|* z^tek)@!Sr1Nk%BlD8+l{WMwNQOLMm5NnG+IKSwSsGyUblv&!QXYMyr^Dd26q*oVLE zZ_k{2^-xe6QWDX?KWKH%3dKciQ&vz8S+*LzQef#KVDB7055as~d_E!6Rd@lvAj)Bd zXo%QSw4*fFJO?`b0Anly7R?mzvn}yy;EOY@xW7F74*foN47Hp5wWC|dh>KAT`|Zpz zYB*JWZpSG%H2%%yIt{-K`Q9L8_wFs*zR_T$*@ zJU=ffs0IuAEYnSU)|3~1iz!0OLuoef~FE*Icmby zJiQki7p*~JVqRXzQ;`>LW&JDoyft^`CrakmT;2I*ehGtv*;XnDxmvs;2ebrASAYBW zS}O@dyqr7tGGRS){tMsyQ^}|pZGU#B;NYrSE-!qwk8siTzX1nM2bXLs@vo03$}M~M z+b!L|)c*X+FHbpbxWf7yv9l-*>8;^?SRHB%uo@m-{j9m@KbuAQUt%&=8yY)vGd{lm zZaXYl(^X}^@rT{B<_$v^i+Ye&qB`JqvsQ`%e2R_*hoB(<;XA07MMIR(mOlqsMds|I z(bEMg&dHRQ63`z~DVTFAJ&a0=^(X{%m?K7tROTZTRICqSx&*2--&Jz1aB+p|uii{u zMK?jG*{J-C2LiS__=nc;uKN>?ZP|E3{nN3|74NgHi?5ZuJC-hdg z5L4p!S8Cwd*!|^hPs@r5^y2wCt7;O0mYv+~D=If7BHmlJN0f#kP zmL&>k;!tU}HV=*8koNmzo0ds6x7){BE?K(b-1Xl!jZJqwH=fmtM?Re0Uzcrr{?_l` zwv9KT=Vv13XZJ7k`95{r&L>qNeT?yb{AEHSRas$wg_+JBAq+9jruxoE*ip-_6lbSi zy!YwR)Fp{-xb5{<=RZuvJ^!~rMtGvM!F%j&uCbSbs@_Z))-a#hO8oikc{rX1Vklvu zvqG&ohza7iUOdC>GH&bQt2l+)eW4nNKAdTz?1^aVP}&(z9ci=<@lNl+Z`4h~j&8f@ z{J`hWtaCrS1kvk1RGGA?Kg{-)%Ol|VbC(a|QpDyijwBH@XR`wrfs(|iP7*6Nz+n)P=y{n|p)T6(S{ILmMbpkV= zWn(*eZ2n}8+N-jQ@Wgy&bk$P7`P1X~4dy0KsysH7p2#)W@Wh~I)5beKKVRg%a#p#k z-4^cHK@doR0p@x4xOW$J+>l>p2@qUIW;O`T6)9Q+v6SKY``2O+TweSq>_M*(BZ~aU zB_2Y(%Hgy{y!DnCA%c5ey9569L$J~w_rBCP&Vp8!U{@NWo-hAg%`1Fp%nEZ!T z)1lgM#F@Yfjfc5n?OM$jBtT^**lf9?3OPB?g{Arcq>5EzQ)n=q1+G$tO&?M&dKV6G z7$MidKXOYadc%VPwO`aZ$Xje&Ssz~Xj|SOc5xpzj$cEIS&|{LX?2ab$a_yiRiZUU2 zXd)4JJ`;_+3FDlHrS$Kij79Iv^n-Z`u@|iuSR~l^^@az#-R<9*9S_#VYXF{)d-{xq zWrlw^IPxaZeRf*o&VRF32yEj@s(+xN+F%F)QMcFFaa~O%LH2jMFa+9~I(1`nJ#N(P zg*T=6b1B&Q8`5v9CbEx9fHn;pr6gtWnC_q5YS`;vaT_Mg&Aog5{VgsU;QB{ROaY6? z4|Pi@!wIx6tRQs@CjRV{&@hB0)6%Tkvt&|Sslvh&KpJSzZ8teMjF3fjluU*IA1bc( z`#rE{NzL4->Ao9K`lO^{hf9FT*HED;V=KAkeTl*P?|8pqRM_xj?HNz8)JPhoS&;bs z%=Od=-I2;C+ZE&56i%UkR<7WnO`>VRFk--L-NP%}Eh+Xn>o?D9mx1>~&EpvU;)Cx8 zwNg>YiC1DWLex6(e%}RSwAlYs0JZ@Y!?5FMX8^^>-|T6qxidi1RBjCDy!YvR|2h~oIeqp zYE3n}TZqjc)R5#@A8cvmhvbiwW4ryy2J-TC)pZR9Q)@lB3Jap&TD2_=CAfVeEICDB zGPjV*cFzZejQRw~n9?=>inuhtp$_Nve%D&rQ*^W4TD7vcE|aYO3jb8L?k@>M5{p%r z6J$g~If~Ey{YanMY7knAr=?b^YZ%eUKT4%)tE8Z27J?m}!Qyj)3hIM6bz`K&v6D%l z2p=`wFggsCCaBR1KW?NX2l|YNV4N4V)ySkx>-_+pE{*GS>Ex3H8md-G$ckYN@wjV>(LwZfc=NGz9b^vBK8UzuY6mo6 zZtK4qjv5n<+Wyo8F*1DXhxHpX2&TQncJnH1o5g}#pT-YZ9pAkD!1&Ua7zqUDWG*N5T4bKwat;j_qcIw^QnPOp_~ej z_%((Oz)bbo`dt}f6xzit!L~-EdrO?JeJY(e*_6BPf)PUPYMyR2^B8AG}NXXG<9E*E%1 zva%#Q<9UfQd2;-Yw~>_b>#1A4M&`PvdY0V&I%1j9GB?h{4bW(s=Nf(xkGAa0)H5e# zjkkyYu_`VN)&Q-MwMIE0j#);o?-(vL-%ywKcWK^MK=M0#wHbc9Vy!7_Tc`T(6pzY# zGV58^KeZ`@#Q*Wr=cJo~zdd9mhSa;i7XBxk(dejp4E%e%mm+<(gTb~N3}8vw zzOKC^k5|S{zDkS^wOATz9=bR-U*`R5?yyCNb_65A%W-ruu!1ro9h#7neu!VE(e<{} zX{IA8q9L|1sUW@eZBX#=)Xt`bUXaw9>rUU#}jRU$bJ$k!{46p({16H;s;U zKflZa6#L{jqoeAHXvRqVqV>aSv}0!Tq(m;?iwR+>r9Ojv4Sgxh^Y^U>O>2bM>Mn7$ zX(uQhT?uL!0j|TXU8niO8nC?_`8y!cwJ}wR!1dk&Oc`toUfb5`Xoo%&!{EDf{YiGT z_Hwf$(SUx7(B=#md*~I`=>>saN9+x-U?Ic?AwhR6JV(X~9QT}N8<(0{&#U_T5%{-I z#?q9X8!3Zwj+r#igWu-L(w{M!(NpyxFQz`h-5 zMQh9MG55)>&|75@oNO@|k4`34Ub9f*>RadazJ#=e*G?aP$FC2qvan-8)Lyz42b}6z zm3OV)e2|V7M1Ri4(yQd*IjH-rzs9Uozw`wrh3_@Yrk2SL6Hkz9Wg3BeFRDLr2Jr+S zyq!Rsg;FO1%n-i&q`%HAVknOETSjATle&-iYihUp&d4;%gtm5|*;JbeWtUA(fL64O zIw6g^9zeG{L1zia)ggB(VKMFvl?vDv24Gld4|*c5%2F){@SGKB+kr=xaYxgOz=k=9 zr7c^02Js0LV6(<8%H+3>98swam=Ay^%>7!0ujBSx5^f@^J|(S=Dc#sKLW|~Ovy#lJ zX+ZNEQ_88>*%NDQ5O3{K4` z|JU-;X!{_P2xw6mh4X;1>4^;x3VO>NDXTr=-}3tfzVbE(6VP-KmaGBy6~K}8ZTcd> zWkTrJFSJ#ls@;Gi^_3S#hO4!7J#QTQMd*R?uPSe2HQr++&H+I+Qdb4+qxOJbBk-DP zW2gz773fx+m>+>wFJ8Z(VV~b@8+{O{c+b#bqPiT|Z&Jvb&|N1e+POTy&pQZIgS@7N zzT$u>bnU+ZK1%_6F=Lcp=~QD~#XP;2^Z;yt+{Jyk*%+VR23risN@#T@u=D*@4@ACW zcuPGHfuHX>-|JXSme8+0z_`H(zRC8ghW;LGIDPEQ-2}mkU%~_aZQlkM8r91Sew?}R z@BM``{Zs^rRkOvU_UsJhK+rv*kpdkZCvl;o9(U8*`A|z zcX1n+iEO0~*w&**o<)a#$&SpZkp4MX+to{YJzUY4gK(+?&McpZWffB!+cNkaTD5J~ zi9lU_<0D`D!QgWbB^z6BPxV#Xt(aV}f`jNX`0DmS20U~k%>L>Ab5D=jKRXi?K`)O@ zofB>P?@V46CvrJS4;eY#u=&V*X)7?(u)J)UBxWINJ?In7pVZvF zK;m$-wv7(ZH47Fp8LG~RjL4ebVI%wb33k3I^P3iWmu_Az+8paG^Ok@=A|Dz}KY9IQ z*X+a1E2k0PUjEyQ9We_xe<$6$QK&cl4_~&tO_dR}!icv#0-fvQ|Na4cA>dgi;KCEC zr`WH^nLl&=^RoItQ^GksaKJZFNXe132CDifz+XBSVi80Kt&ZHKu_6$iu|w83FL;#3 zRh+TEyFQ^U1!69r`?#Dk+VI}Nrbg&93Nl-0LN#`ToPH0V zIj>^gAoQ(i)99l#GQP+f99QNnV8#W!yHaF+p~IvLBpJy@J@_X<6qhNi!KTL*I_hmj z=heXUQ#M9-K6qOAlNulSH3&DRg<0hY4dX$7rzWkf3LFEms&Dh*N{ALh(I-B8F$w`f zHw$O=_%^@xfU3R@CwJ%D3u@dZq;Rjl_e`6pPpZ)%ymgn?^n|C)9}H?L`fvh4tDLYN z#y>mfRInmr4X=l;wXF@e@9NwsRB1d&ZLq4Ejq!@N{&%{=(l+XmdH}8v2jrwbb?qfs zKNuH}xi^eGce3*~#1rFS^PA}gua20HsbEvHLOwLze=>aQd=WH~scJWpwco$pMqeM0 z*-t&5cDdN#n-SdBeyG}X`BWz6zvl8vc2L{#nERWKv^e8}r}t)~OsPzBx)po%Vl1oO zWJ0Rv4N^p{J{(!vvsda0^xX8f`LmY@sp_mrfj*)Ruy& zG5%`zvseB(1i=9--%8Kuf|P2=hbz+fG+(3$_FGZ$ECuuEp3lGSuS;+6M9%I!gB(O@U2DgRo?tGU=ELs4*HTNq=-+u z60EoF;U}Kf@tbC+pW46t2voM{N!b!iR9;4Y;!N>60BhHZ^v}UowMfO;r5kNS+r|Q@ zl%TX%S5`V9y5baXUaR^P3eITMd>PPk5%SjlB8C3sg_Bs?uTpo;@SJ~Xv3|u5Uh(U0 z^KA#i_`mjzOS{ANn~~*))or!AzLs0NYt*1#HEeZnz_Uz17`Y3kfWEW9mo3In{BCQ)evTL~n6!=^WpwoCqQv{8^La2B_^ zH8z0^#JVtm-2U2~c3Px$?d;lZ6log7lNmcm`RX-cX28c~Zaes(=P5w@SN_#(kV;MY zeEC5CDL_Z`ApGvC4J{ip2>??q0F#2^;!#b2O$_w+9UM2$X)~TdsL^kdAij3a>(uxD znhJJT@V%-6DV{1zup53HhN`6a3=JJusgWu}FNUFr&2AYv8q^*H9CHGUE!c)LD83f9 zsz(?WZrUwvm19D?7C=@qmz+2BEu16HpZ|ukKNcgJ2>x~(IP2}j{k^-x1KK9bTYLn8L*jrV;Yl1#YhjIeG&Yv^QFMnd_ z?@M^{d&S>1MgSrnE0%Zc4te=$h5)zyF9|q2s|`4k$6VE$GNT&WzSAb6npyZ@VacZR zcEe2P@lyAdVKegQnQJ=1g|G+XSFma*Ek8Y1oJSoXSEm|vkLJ(P1 z&RGr*eDY-K+Q#fZJx4LZBxU8l=aTayVo5 zF;$`AI=!$@PK2lpFsOaLu%`5R@0O(IvhVpW2zB?a{f9vVno=7k6<>shaby^@bTxZ9R zxt(yKd1rdkPXELzbwcIWf$`#3qKL&kmIqTG_E^=GExy~N*>Fr})$;X8dqLgO&PuR` zQM^-o`LD&4RW)&9YKSfn5P_i(PVB?_ve@?*KGW7xRsawh$wh-OdKC~j24nOrMJNDi zr{Jq}&p%ASs{pidqhW)%w-ug)O#noww(cl3$Vx~WYzk~H-fO@M%^JXuY-?7=0T`o4 zJy|W*%A_n4QO)h^m)(?`6!epwCwGn$g1$xt2v+lYz`cnAaVa08*-%#;F_$tb^DRsX zDc7qIhm@1vyem2qVl|13=t(Ggdvds1=%H0+>*o=3Rh9)F)Eu+kawKs199RA<^{P*Z z-1mJ;7e1php{Ov2&{v(X#T$EB+n7hAp=vh3Xi;L7qd83Ki95AwgPN_&cH!rAKS<;T>)AzzUJBc zLB)e3mGVL$od(F%FI1Hu&x3I(bWj{T>ON?}-yXXI1zq@02aSg^1!0@1j#0DvFEIrr@?#F-KPgg1um5uaZhD&xG(B66Un3lin$WMe_!@&pI#Gv!pZ;sJaN$$mjs*b zm=fO+$8MtuQ1oTE>nTg7PHza^I)kwD(9DWmM_@|waEKaub#0{KdD zmp}Ofg*a@HgDXE0@H!bxkle5~K7*K}0_N>9J z+mE&cvY20?<&?IqFOQutr(5@|ngmonOm_MHra9@f3rt2?7?Rqky44hVm?khb?P>*~ z$KvGN!iK&x0T@_q%-EFC?HwT6xv2Q!PV%{dc=ra)OLj{6RNkpnuv*$!K+i*w89_=S6eOyf@J6l{@xvOfI&J{(Gs<Uh}N_SGI>)s5&37$8P8P)`^-y|;GF&Tt69n9rspi6HUz%mom%@tysV4M_ZXNhU5F~syriU^H_67~*Jr6CmW$A!li_dz(Ixar9#?`BH2__%_&(!*`Eolfx5f!nB&7 zn6F%tDsu3uOapVOvZ<_(WS$s!)$PjV`yZFT`Yn05b~jJx{AP=GYww_)%=_8Xk}d7h zxrgcU??agYlBf6NZ5tNj#Sz`x(w=~0&16e@{L?PZs!-Oy+j{^0&4|P$2|WWsWNQem zB3zMs^Y8f*9hHmgXcHxmhD06IX9-x!G;p&KQmE)FaMDHb0nc)MRz{7k+o1^97x(Fx z0KUrC%ei4P8k|~O`CtC)a3U1*%uHW{Q|6SmrhQGI^x(t9V5W{aX+xusY7CY-oXCl& zkazt0RS0;S;G~}k0lHTdN%X>N-Ni=tMCDO9uR%CoK7%!sa7irmNrcSsu2lVs_-*Z* z`rB%_;>NxwB-JuiG6T`N0^)Q}!L^{#Z$R}SO{Kr0yHKiM0jkz?sQ6-6%!SJIGiZ1m z1*o5bJnan|XhP}hhwa#vzX9C#IztKYs5{75A%&!)`kh?L`%zLB*Qi~F%jKK)0W0sG zA6?V`_*oCGy-qK}!n7SxDG4Xcl9k(H9SxmiaWz_`u#E-DQ);+>lo0ly{)qrmQ2<(; zoZ^J_0E!)vX^sFyPnzo0eie~;rjXlha^mmH&8iF(bDpp9Y*THCNpV3z{XMIA2gMV03{(x%|p)4uT#Uxj0wRSmuRX8 z0Hlb?bBY_y{aP*ks89)GrYs^}z}HC$ z4h*$W(dL^dN5~u4h7H`gXHr6iO#dRHJ3NZN8vsU6o=I`g%;l2$>nL$Wc9%e0d{R^> z-H;+DIMG#)iM2?P_$Ll3bBT0Q!T?1>!}!1jrurl1H8GV7f-Z=oA5D#0N8MGYI}G5o zA1!K`kG+`SI8HMf13f*%CetW}fq#E&dV5HpLf~MNYzUIJ(y+PwG z4~#+sv>MrQ%17Bt`QE`%zYKubK)?19RE@?EH(W9atiz>|sSd}{4*Cx;m>xCVuZ8(xl^hHhyqB4d$@B^l$YI)a3e-@PfZ_z| znoRpCEzgZWSX$i>=e6!X(6d}lT<)Tk1$f=5Go7h3`XR<8Uvm37s_8^DZ3QrcX0eT< z*xYjHqFBG`tdRu3Gn51h`bj7DH2go4)24}a#Y%RV zI-Aq67rLtzGO)zw=_8*}=b(ktOnLL7du*H6bQQ{=!KP7cwOsGbsOeX>_FNe^DotC} z#KPZGX9q*-`;)GvxewgLHf@|HULQ4$*A8B)9kj@0Wk@uvmf(Zm$L70Y=DAkAtWzJ% zf*Q0vv&A;i^ppAf0z;-Tx5XFw6HK20EHbOFP2-E%V5a^^R%~3y2&cfP)Mehm^}P8%HO~n{^`_bt9>L z8!qXl-gm=tl}(j8-8U9s{vLbHq!^wWpxaT-1J0|ULmW%>xo|P%SsiL2UrrE1@yh)Z z#5<>^b)HYCo%DQv1q~#Bx6}w1~rg(J00WcDQ=f%#@P=FV`$rgpWyr0B!a01NIRIfW|TEM(GK6OqQJJjaYo5WA6sMd6;9T z)+uaC!f!${cGNtv(90;F2iO1!3@)q~Dqg6UfEvw_n`J*JzP5vbL?7JV7>n$B(rEA=E}9hJC5hHE(^I(XYzwB{sx*313PLb z{;O=ac+VDN519<3FKi-KI+xWM=bF-%AKRCBv~mv=EH&8BY&5TK{lK;Qg}>{ga(@Z* ze{J{JcrOOy@SqiXTK=phY3z(@)6FJ@@qbxu;`G zM?Jxjr^+`6*8luEz4!dw_uLD=u3z~3`vUlUy@#iSctdik+w?csr1ji%La8DD(?s{W zXqk7>`o=~3KT2^))2IRX)!>rPpG$17=AgXhup7PFLgudCU4SGV1`s-#QU_g>LDe2IUfrTkAzrPsB6hc6Z#ZaVPiTAkPR6M5H9 zd$k=hyn5E_#^t;l*KXWUzU+K{qa*KT&yAb+Z%8iux%oKn*3%oeUOZ=B$UFZk@AkVJ zw?F3HlHIuUIq%M|8+ZP$xc2DJ9c;docvHH<>$bYLOebHa?=6{|l4)@zhWY=4bg-Xg zHd(f5p<66xTYYBR*xv02ENDtd<^Km#>;C~T837OhiV`qs^glT9P>EP3#Dlc>77ktM zF*EirWTcP%|HZTZKm4fwpI~CCu2nTT&;(5X@HbLMz3sMLNz-5e&9JL~uq@ z8oLo}HN||1iHqWm%7T4N?5eV`Z%WQF7K(1*t?7&a+%rGf$sLuA3`55lI}Q!A7|=Vu zW-=5-2^v7)ldSH+^{qs$1+1F%H5RaBW5ZW(abK$7~PRM_x9S*lv8;yY158eLdf4fh+A>49ViyhTydH zGrGA;AKw1R{7f0|gS~3KCZMab|883^=4dv)^iAeOiX$1koG;^>|9#(kz}q@g(5UWv zuefVAS> z*5{#G68Dg-zY;hoSq*6`SKs@1qBgyIpQ~nC!Jt%x?;kwA$jv{x+f-oQ--ag`9Gx87 zO)9V&-yFr}1litfeyE}aBLKwH4-!W>s-iTi{~6;fl-XG-;V*^@_G{p@CtFp>?%jnl z@DOht!tZnCMVi^C9HiR38D1L@x|2U1L2>V9ShCl+JrvY(HeoHwFxg)t4Hwk2AwtX= z>wa)M;>mBL8*NKf&*7TjrR`jnAP;cBDN;=Z`ZI9>vQbjB7NX~etj^OCQc}0ed z{xHv<-YNc0vo)7ORNbwCGB>p)NSo_iyYm7ZfSKhrySvKE-8{UR;okphw7S(o8jg2H zbzbSUIvRFmOJ%vIfIkk&I^#7U>v|Nx$nVy^MfP16J4c7O2PQFNJ9joiL$#XrN!h`N zMJn=Hvtml5pdWPk$P!xi1_Xq=CFNcBj=&k-6fLb=21zEK(NLy_{;PO)d!WnMT&xRQ zTf|j2ug&!w2eazofYY^D-^-4uu0xhr@wY~05CuJ{G$@!k3x`;Z8yg>j3p_}w|<#kNuZF@ek1t*A||n5O{SuTq>ZI0~TYo+cXU9a9WGy1mlk> z0E`w9Whly0n1w_%0GP!st)+Hct$FcMd?awxuBFZNFJa7bPOi6SN3&3iO=#6V&n9o5 zT1!^z7dZc}BlB7k@c;J=X!Ah&sMX3k{=LgVJmHD(k59Nb33_)8+l= zmBLEuriiJZG; zj@iZVLdxfvJgiwZvPQ2bqw=tlCmA`SBp>RB3l+!7p_EeUBFAD=+?T_2*@`O+8klI~ z3~R@=d;epTBWuOAYo@v^yEEocE4jHEty|5puBIqiVQx~ZZ0e~Y_`EdXuXL|9UGKOCsY88la}eJES=p-7r)GH{d@RrnrN|0IQh zVh$O!pCE|AK6J=ejEe-> zXI7MK8%B&?*ISnh9Uq_WbT4~r!t9r+8K7!XgT1&XGhKeI8o2v+yGqsso{EX%>~Wkx znp{2x`?V`0w{sH+gyjJ9URJ^uW%$y(TAA6_uibF_F+IX?59T65wvHF$#~AHuiH?Mr zlmLttN8r_{5SZ5s$@9eywih+d!AwEb^U(dEHfI-aT22_B>GYjUZ_4Srbkv;yVSajP z61Sh)tP%$3<&2hMr2@SOh1i?|;51IsLfpnXpv4?Dwf8CDN&qGu6rmI~nt|@7Ae9E` z?lq`P!)Szv)z%CHfwfePeiY{*#?Y#1n!ox3)J;aqy=G`?OanqLE?y#Z0%JV>7-rYZRN zKG{y~LVrGo*Twq5CogN^UXwHYExY#jD8tq(3n;BDwaP+{T&R+{somsuRH+t{Fn|y` zz8B`J(&Ra$E7V|h-8zg@d;sxFX*>CocQGyW1PE|0Bb@+tbGsR_QrcyTJG~)hD7RbW7EeB{z)Kx}LN=`OZsCEzOsJ z_@Rm>TqI8biv@;+9GPm))LL4e)bLKIzi~xAT>KISPF#1d{CT9*5M9;_@B;AHM^#jZ zm4<~SPJ54xXf30o`LD#vvXowliQ2MPzZ7jJ^gmAeF#)57ISEScyGe2ef{y;zJby2I#IYQR-kB=+f<_>+EboE)DMse&Zc;{zz2|srG{j~u4 zxM5dGV)g-d*GL3Nl!PR&=Kj3>X!+PW%FXi=nK_r*#E?(dMXe0cR(GBOp{SuG&626Q zV=xAzY!^0fJNnk==Tq#1aQuHs2_d;Ibw;~b#kT$&A=EH-y(+WV`HFmJMx%Js?K|hW z5ps2hLbF>?RFPk_3N&+l4Y7##wWXtWyyeT!KTl-9T(rn-uYORN4Y4JvVtLa}(2iz6 zOB4uKfc|V+oJ=)^sb!|QD`rxGF=;0LvhwFj+4hB80tH&E0m!)4OX%RzI^d&{Wv0mR zl&1j*?ycDUV+KB3Owt8FidgLgCn;ue(rQQOiIliMKnG9&pAdco8?fY&v!&q5Dp0lt zxR8!Jw2!jJpZ{PSpNx>xd6-=y@&+1s2UYbzi9LW7P6`l3fNOt9ac9^!QA+irmD6He zbCb6=3p7>^)gc}^6mqUPprl7t_N#><zBDgULdp%G;3#SkovKYsTew$g>_Lfq zcTp>lg_4Cin0Bd(Cl6Zs1<@GD66)ZjJfvNrWJSwViKbV9&615#}U=BB>AccZsmb>N%Jz9i5Da!#$;qsO+oUE*!P;M`= z_$b*DC|#Z_f=m4IBa@|DLg%FpTR>6^GhiOXw-1!=5)7|w$|>DhQ74TOKY1++X2E)6ngr-==Y5J)324gVr?j=O9d>*`l6FFW#?Y1<~4>j#Km9VqGS#3 zB_1q}6~QWU2a_b|QOvowNS*cG@5}T>@a-0EcRJNB{P(Rsqo`9u$4*%g@RNC>sw|BZ=1KnXtfn0#xFD`@0}L5|?^e$qmowx*Iz zCnkSFj(CXhFU9095^^>I)k@XjVfs*FJW5I+7O~|} z-dpk0HXvMenxBp7O2rh#LxQwo!Duo_XVTmn0bgOPqu|co8f;;$bYzX zk9n1VoKw<-d(=h#t040|37_Y-{g=&yqWV?8D@X-WKq#soI}N)drTy0=CdHQI`C?k%d9BEX)O@lUS#}u!j7;*70IgT@@ z|C@dH-#M!IAZUNZneu;eb?^U72LAu}ubnrCne#Ag&Zn63oUfTvQKnRsYUEgG$RUYq z=9oj0A}MnyI;$uZ*_=rZC8<=CLMKg0bn@Nn_5QryKYafKw+*}Yd_Eudhcc@%U7}HW zJw}X)d4R}0LVU0xvHIx6c2<3_`|f^MLRjjBD5C>z`|1gSCrZfht(Mr=Oqo@v+9^su z1dcsUsIRMZ2(y>{=V5flowq7cUwa8zf7BwTS>{MnOvq71L@?$PfK_3`&A=vEVk(;fIc%C9*Ix zY}7jHsUxG9!aRh&$O#ahzafNMvuuG;+>20uVDwJa9kqx^tS5x3-vGbKmsVjP+tIXZ zdj&y)&>9kteP54O+An<`lwc`%{Nqd8f$+c*bkHb5nW=95#iRt)=Rp)&Adf|Rp zLJi^VLsZl#!e1*>gNJ*Rjrq>f07|4Duqpvw!S>ru(NVT&s8o3oHP>`Vss!`73I6$$ zgcnsN{W2V2>3ctxOuY|NfN*CvdyuHDy5~8rY<*>d)y9*y?ujsY2>AuL7r?W)l>*Zd zVHf!5+|kx!hhesq&MG;9d>c$k0h0pMn3>p>op2K4v!}RS_EX36YBw^cx5Nm=%M})vy242JG(UFum$V)d$7~$n zZ`rs6`c3m7nW-P}W=UkNXZtg<wxsiQe45$v+ z%kDIhIP=`*O9ApniGUs5vUO+rzm^sp-BP9O43-&O+=Rm}vn0baaJSD;6fhlu800J# zBA=mHxf(3HqFbx{;%!iL591qc;IK%JVxx(cQGAk_LulvY4? zy->=oL>#$=y-9#QJtOT;EhqhIW3^^eMUt#Mr=oM5~Q;jo#Zd(Bco`)CWFix z)XD%_=7aQz5G`21=yEAY7Ffa)2P|!9qF#dOHrEvk1bD6k!NW zCHs8FJ&MLRY;5}Oqu(F%x~EQ(55SJIWc+!+gb06_fC}Wlcy{&`N#yu|iRKFNp&>5= zOHf*L#Kra(Z#GVzV@>959LRcq5B=*d#(4m3G}%FTUAEV*@#>46Y?zb?xddUOnow7b zvmA!;?IoxJUY5!C$u!1`fp}n?Dy2D!aN<68yD zr418MM?slwe4iH}O7|7ogDo{&g1X7ayYP%9s1>zqFQ$%ye_dZUzU(=Xw3yNT#>Q+*#n*<(MDSr% zBK$H3W<-{9catfn!WUU;&C?i)XhBh1!oDfQKT~3yTCkY{ivrF6F!50UY7xS`yo%=t zMYpG6ulP~`K_-p;&3vsIT@xmAlzgD98EJ zzLueMV60hRCIH}h@wqhx=JpFXVEeKHllcPx-a{xF_2?Bc;WYWdqfw7+m;dg-Gg794>OL*{CeUG7T^hGof_CJAape<_v zs9Z-fa~k?y{-pYfI(JyMIXQx%wC~r-NlWhe`Ht9Jo6Xpy{k{3+UgTc+tqfPC{lDJr zs)WIDEoHg;sNfIZ1_KT5gTD{wyfHgk?A%|8i=uX7WxU_4PLFB_wr9**MEzL0`ew|@ zn&q5B3j7v2cyisI-YPo_^e_=Ph*saR6gfcQqkHSi?I0=r<$mbO(S*yXX%mSdERMv|lDL z^(gcHZg9sbUsucLk`yj_7>4Bh+XU1E%wexa4q2#B$(=a0w9!67mQ{HWx@#jqryfpY z4s4-UuzY?R2!X*lxk6i{`nmbxlEa(%0^NRMime$!+lQ5_w*0$g3m)AgzMnKAmu5;= zbTNbDT54HObR%f;xxeavPXb8{>Xt{ySiU3>?E7wMkZ6{E7rp1WbdBDlbhLeBP42lX zmV;h1pDjm#ep&d11f(#N6jP?Uw(p`D|6UZf^zi!q4ei(9r$UZ$-b*2yu9;ZjUAaTt z*rmleb@0LlH9*2^-a}s9qRgcEUFj*QFU{q8J1j?B({NFBbsB#n>m@U!tonbaJAT36 zvwJ@^K>047UD~(meQ$A+FZz!2VCKq1>)&_y+O9uC{wTS-mX)>t<8#c{Rymy*n!Wb{qd5=_xkcQ=KMK{7D*{wY3{KbO0*zd-l`ogbEtSJ5+I6 zY%IIUPHY4-`~^}E{K;IMgVZ(L-cjf)IT_FqUo&X@;mF^!y?gq|u`E5e8i~KT9VOFJ z8!fwo@rkVrjllzPkF=*oF5T9<9~N%q;S(h0A|3#yaS12|p4{0AN5f5HN4Iys#b5r} zVH7@Aum`!9c%$FO+!cQ`%<8QS_kxGj?XjW*Su={yiJtZoVo*PbtZ@u`0m~HV{Y>7Yq`bxd^;SWRA9QhO1m$YpB?7R=&ymj_{ zXt-mtp|4@QWz~Fr#QQSCk(k*h)hTWIfQ;Qp#tX|TgzlWBWgwFY_pclx=`&hZnR)jf zpFkGryCz)pi;MR!l1I(EXiNCz79WgiGr8!hn>v&}`=riLI+AKT=v~z7AAjHKgE9T$ zhWuHD@_xq;8$LumDSA*=<0yT}HhF_z`M1jxFKsC=IC05K^P+EEh}GpE z@ANlreDSo_BZrECPG4a@*micWbW*;Z)d)g4&W2~Zc+8AGa<*fCmjf@Qe_u`l1 z;9rcr&wm)~KC#}?UbB{iI@cF9+{|h@`eabAquTQ+5*m3#Q_Y2!C z-uB=QJyYAg)VsZXGVDOIrsbRE7kAoLubL%(-M@Q9Vx{9H3WVMl?D=)!C;zWCHAAwE z1sBJNfuyFab$?iYufU|8Zmx>gkiJxWk z0?t9F?iSVvnY(KXDN8#uJjG~tXkBiyFiZYb))t<$dxBcv974)1lQSAF?2 z>v%`y5u~(qPIgy>d)Mi)+-;VC#@{(F-^x+WZL4Z|=?|_ll_Mol2OAhgQBDb!<2e@& z_7R=twj53!JHILMvS<5;fG58mTU|@MuxDLFf8LAn+Y?rGxrIT!5-Qibl^XXL9(^|p zKYgw1{KXU1RnKQq5!diOrqtGsLN(Aw`Qw6AUF(?(H}%W9+CHL#fnbshF z(6&9>;holLHIu5_pj~ae0W^AaYq#fR@~ur99*?CRu{}@~*Kjv*=33j)#IN%csO#UX zhuh1uk`ISH+`Y&*Q(lZtZkwxYUFq1aAnMwvWqiXf>FTq;<#Rrc@-xF5Th;L2lHA&- zX>T4~Ka*H`6B+%nEBD0#o88NIs!bM7zD~7MT?_o_vvx!j7kPL>er>TjqCxbqA#FCf zeRYSv4Qb|I+MwT?U(fo|ms>iHENmJ3{^V==%3bU~{b5;;NU+Ozv^w2N2w*0ccjwo0 z)wf8Z%{)yZlwFOf`gI0`N~$3ekt~-Lk64gMiF{-6t3b z>u#6JKRHK(aW6`p{BUCbX8p;6dv{dniwwc*nT{6)QiOTV)<&dNV-_(jdn=Ur^Ogpl z46A5Wi)_kKp2Q;F8jS^7l&cUZkGlF&LKW?(ki&NE4k?`i~*H+ zT8LJI_&huHR3U;eZ+YP+sQ*Vm_IFx)B&jLE%w+M^!y&Q1S!l*AShob~4N(Rh1P0@d zz27?&bhJIe8i2e zZ+rXLm8U$I?*9rr*CHL9hn^H>r%B}^y9X+~b`|nvBQe&EFcO)2pcSsgQbebKVG0Zx zCMIG@rEp2(n7@-Y=|XgtdoVFaiY427L#k-~fN{Xdb5`gaooYsqyIi!+ck_lrWrO?d z@4nJ5lAiQj>VoMTRETZO?Ik%^{dnxA_81h$_~@RiB(kpM8Fw z$=u)n=tv$y0~|Pzg2SaCTJK_rk@;v4jrie1;=%G(a@kn!sY$cXzj9hg*-|N)siRya z0u)NkIojqT!^{3WwP~O=-zAds*Qe(MI2=RBY!4mjkn(v|_^8Wgs3+P-iUm263zGnd zjD?Rep-_VFH@9QJk6hSEpv%ptXNt2x8buy1ybAiJ&m*J)vN(~LwM{FJgrrTCJ_Erz zTQd0GP5mcIPuTr_xaG~6LFcn3gEt4qc+ioo%nc>k>qZfQw5;o8Xmt=FLF-ANJ;aO9 zyS(7e{9Gp@Czg$pY=T*)aFhxEkA*c-fmsuBPdc8Bqapz;N?d6p1Lp1l;D-IVZWYHy znE=p_!iZp&qOw?QwlZ;(BP&lV(K30t=0aGgD97F_(?$f7plR>>orsOjhCB>~<}h%}MA zjE}qw5wj%{?#9Q^=b!FkbD~ZoiC+p4yqugPlg0s*3X%AiWlRj&I#DzCQ&DOGFdH@u zF%N7Fdn(J^o|L$KXV2DMPYH@ZrW|#<6R*UpeKKu@tJ?Vd%%kV{#-}?UZPl*`@Og!B zxFSLnQO&+X6rOitCKmbyLHP`K-1VA=bQGyrf6R5&$r{%1kjCi=b8H zo}BU}q2%_lbLJB{AF^e{G04M>6A4fjX&2@{^el~gN>mtidQa;{caK)I*xEdz%}op# zK(NP@S-A^Na8VC8g(eIo=4h~TX+mha5CQPeu?FxDS1*>gDpX8%Ct(AXm}9$~UccM* zdb60I^>T;tayBVI2fKF%>%h@G#ESK{5HUf^4h9Tf|A;~YOxP_kjyhTPA0>7kP`E?i`sUz*B(aiamcF zF?7}#Q3Tt>QXIV%}#1v(}j{NAf?ObeD=$+z^|)9vG%IZr_u}H0cGl zZ}aR)wW+SV$2)v4sd<+|naznAh&bh$5gMM6mc6}#^Yc^TTPKL#pPvChVpvs*nYpt+ zH+deRY6pnll~Lv{t8i9QBZ2{7^!s70)^A4nE(g#Nsx_QM8aFU6M}qpuze)+5N2KKe zGWlnNqz1&q?bd0jJJd`P4GQQ-s|5(xw0SIRVpdYpd;kk=5{WUUW!nDOor(rjk5?@n z_s$>$+y9?Uiaq9Yad!W3=kCa9|FrpN>g$765hr!v2r=Zk0x{2{LRzE9r`~w&R`nuq z@{tf-l#|+@dxZ5ih1Z`^oW+J9XK|y6X3WWj|VmXV~UP) zNmQU3x#7xl$c)-Y;=Mg>wJnj4aIC;2n?CecD3ZGieCmy@;AOL$vedBy8Lnoig2>;osARA}Dw-u*pI+xVyXf=YxgMNf!uyumfyVYRQN~ z3}R~=gzHDlpNf^LP}-;jga+iC)yR<`mPoEFh<0xlqp8wDD4d_sHfk}kTH=!9t_~Keo20n1Yo317zGhw4b27tWD>}cTyX#Cu(ub#RsGF&Rvv0C zR@@w;P-NJcOXw>r=V2;XnD?hKbOPr`EQhoR{6}$RyNHtJ7Q)Qo!Wfm4=#@=k za)7>2hdqnI!g;TGj`nq~BgEPjl6KVQ%C2>MGcBML7E-_+M+rhK`8X@bL6mud?uAgV%28+&wIWNd-bZD9dV| zW5~wrassxdqYzU-5RNM_}VsF9Rh z*#E+(nW+u^us5*KwVW^_m)@A`Fv=;lnn0Pt4w6&22c*^_*g5OG5TYKAPf5}~{i%S? zxLp7mn}cu^*SSU=@>@lKzW4-q9_)rU?LD%FT!K&@sGBg`OFAVX-YM@#m9O_V$%Hv!4mR zKNDq&Hs`|Rz4rMOAL2gynZ!)J(8uk=jHb>bD6G}+YOZZZor(t9w;S5541ef?T&hU9 zbRZF*n0sL4*Zz^^wU|C&r{=GIJXCi#UTKb~tl5lumQ?K}3ZrfGzZle9q zn`XJQYvc40*%l2S6SB?ZgZ}>vgq1I6d@nz8*n15S6{}cAk8%PdKY(;4HFujX+^h98 zO19#T$eI|ZU9FTH(uN939_1)aD_^%VMeC^HrBI-xAhn9F#Id`T zuJaa;9s?zCI;xRv)K{CN6bP=^B6B_nNBT1bNO=M|3`N*Lro&J^hdP7T#Hj`Z9;YL2 z`^JId)^*)7rkBHZuf7*2w!=%pWYkS+hvh<@2kJ|GHu3oiJE|&-J2w#d!zVc{LCecLg3)(SlJzNBMRTnHAZf z!2S@1Vr@GVCT&V(L8qbY|bU&UXupX-KZ=ERK-0Y@7*-`jhjXiBBIp2$ zHV#^LDTea)^}LCF{=QsxcExI3^CQXnsnvrN>xsbq?bgqB?TxwgY`gf87Ux(C>ygSq zOZN~XNpbas!PS#=+g~f+@#LJ-1_G^^K)7SK?*Qs|}SMoH*kJyqe`7fVm>u)I5&fe+pIx$;U z&-7>3c}W+?#U8bcw2ANZxNcbwUf@c~Cfe~6bNw&3MZmWt?R-1b9UJSkV&J;JZKnOn zrn?2-ln5XSmEl_p!-&mgEIwC8YkBR(+*EC|&g{sCe|ixQE=!B{+kV>t6DYU+HgDqK z(|CwDk7}u!s7ylW*tBUy?aQc94?Xct^YSx+Y-y8tN;-wx(%7l!DoWNoNRiwtW(F2w z_mRse_=LtT^>0nN&N`G_w{i8U8s9vI{}?v=YpC`YMoZp3{>@tbc=!x~b$ zdUq|Rwfv?Ky9|2GOK%jaO_j*jtkCpB{E8lY(waH7(uo*c%nx+jD|Z9irgtx{D7kq? zsb$5}MQ^k)$5BU1{$f9R+tuTd(RJE&z3xr!H%saMd$n;<1CA1+;}9wIsgmKKU+K-V z3kvV_jBx}0=(wY2Lv@WE@;9snT#7$=_ttxporeZ@eY;uypp3H4c6ET2K305PCtN4& zw0G#^xcu1W7t~;{mOYzqRZg^B_)+npKXTu#;&ewnOTlWt^Qn;nMp?b$d5=xLmGUP( zmTB3{7>*ozd?LSp35;Gjo~P==LN18<#P2L6?N>zg-#-x`rqY-DOP&XJg3*5rjO6Gk z)JmQ;aMe`x%L(_dLG?5;oQ%e}IpejOx>4@763R#SE!7&td{`f7w7K|{|9P{shF)7f zZa!Uku->udgKzSSv2&088_d;>n8%IAD@~s^I&b~xcUpC8gOcI}-;*D=UaZ`5d7EN` zt?tL)9}OpNd@DYUNpY306lSUUudPR0u@O?@+rnEH60gP_*&j4_RNSnd2#?La90Y6H ze*c%^b?G|eJzpgP+9z7BXFM_v!Tk=n>oIX7Mfx*q-TlcyBc+?CznFx%?40b8xP5~c z79HaGDe#GSzIf%V$v)k~fw}ogx2s}eqOI@mXn_lx@3h3kMEu@4jZVJR^Xh7J(eE8^ zO}F2@h>A@(etG9J=kmL0y2eSj&a?wi! za+;mWQm+ce9th~vyT^_Iue_2yk>b8K|0&#jkKr);qu&XGoJx!GyP(o_%O2y~q4^0{ zX4Db#NB8_;t{Yi#JBy2V^LLzmSCaOiwP&&*D^%;m;^9!W2Y$u*zsqR_RlSB^{TPn_ zEGoLH7^Y8(`!%}#i0vOXdR%+%US(yWcG6_l?#bQZB%3fY;v(JRme)vvW4z{9lX<%Q zz0n*~ipTA|_YBK>W5vA*ye*Tn&N3FoCq3Ssi79OL_I&cVtk6=!uHDlmLVny&!MZTk zF6wY)YAKXchuHo%(zi}^@YKY&r2SVNb{)F`Ri@WnUL!@pC(57g)JqIJezn{9&aaW! zZHc!olVW}h-{jsr*HTa>+f%ottzu zdWZh6SAy8%tJlu%JNRa$=l8nh)*D~;9g(RT`E5fx-|;Ax_Qq6LWBT51l~|YjtVD?P z46)B$7ZW*~`npc(XXly1#i?Cw!UMAxI@X`l=4d*;-B(o*cFpDbrhPVB{lrgr{2lbrdwTZTkWX*=(#DQdk?w=|_YqD% zU%LHWIAv=0((=vf=7T9FD4Erfa*dDGz3o4&Gt#E_zxiBa_28*pb(*ledS=wq?$g5u ziIt4%?=5C}%LQ-#MSTlu@3iW$N!&v*fAK8l<=9hAAocNF_U?%u4?{Wt)8clzdl!g@%QELM;aY9DqZep9vlz+D0{4rGG!e#^{jBJ ztwjazT#SA&DsgH`ko^dl=)1W}epoaqd3$I7)|b5lYb;r<9dhuR+tcLM^Q>0c=~MLS z0d-#`8;bG;HTiADsFQe9*7V=P8sh2T9A2>UwP{frMeV`#A{&xh0;jRZNXsDhQ4R4G zWp18A`bQz7-xg4TkxAdWagQ7BL z)vAsIS{gDaPzyilJ8>`szl1b6S}P&~Ed~X7(E}%a2GDJM^8hXZ30ns*s>5ng(b{I~ z0V8Ucc zxR_6C+*GC`5Bf~i*>^#Ys19g{b{WM2?h~Ma zqlDaKk;@Xpr3xPViyM1s0|rE-O)>NukFwgj9hx}a59%E97~85`H->ZkBT<-oy7Q0# z*9KUTd)AAxJ(mD;zNhE-I}*g$*^J2>71;k_SoU)rr|8m?0>?iL$AAcoSGG&uu;db8 z!OyibfXg(kPde8r3GvmM^zA<*w(IGne{4wHz$cB)I#12Wd$~JTW#YX+4Sw#nLR8+; z1(!eJE`@NJEABhXJo0?z(=Rn}Z+mjXJxD@Glhv3L-QnF%T~cP^k%JGA%j8;pBBS}D_lq3BN(J7Q3xqz%|>0)JAEojN}W$;BXE(E%DJQfcjJy<{c7M0#ds(e3zpWttG0Wst2&7m?a&|CxhtW>7*oCZk<4 zDSX?;kK}Q#es~@w03p|nAWwE$wnB2D984tK$%+FOy4Fr`4>WNp(HxU^T*We=p_rpS z-laYX*#&&m0wc?kJZ@<0zqtX@TSA>JhA?Gabw;kvky=XQhT7w6Ts3l7+lt{jEJt4K zI_5FVqB;7^%cD?#tJZ$`GQJ(Li}-GzCj0Uo*T%bpQTI|oTJxv-7NG44T*{5E%I0H( zxW=GBQB1^^)I4lOXh(u_Sq#H*KxWdT{4H0efA|z6wzdT{8ca4V+#vwOwRYOjIGhT@ zF{O1lbwHinMS6!YD(PIuH?&Oy8>0Cra*RD6sQ#sGOcY34X{!gtY!YtJ&I1vx49z8& z(-q)*b5~JnolX2q3JI}JNOugPONyhFE8=h0nbu2?W>c5MDBUTDgT;atM1(>USGAww z&`sAE;>wFDsaA$oYh8?#t3?TDH$>NoUSptBifwK()Uj^wRTnIDuBo>1C23q^p}=u+ z-RrlRT1{LOi7wL=(<+{cQ9(?eDJ~Lj+pUQ80X0Uiebhi0%>!ELp4yYfbMtWHc|M+C zCVU-Y*$T(EMXN5vew`4^cr3hI7Eng7iMM_L+jTriV)0U!ME5$Qr8%2s&acV_%Xz^= zi@m;igXBVlrwE}x1udt3adP8Jwei&qIwwm6wn2j72b=@{x(vK9Dyr*H+o|8g1$RxR zhHS2JN#|-j18vgy>;(d*iNhcf#vGdQ*fLFC-rX9sL*NJKaMPEq2xfvr+I3+xk}&9dAk`9 zDSe|T>m)fMuq?s4afa910B0y@o-dLu4;xj&j|I>nbZXc|t} zbKrq82ATFk4qgPcVK+sL2e&Z+o1IOz8Q+hRAdf5r0c2RZ!7=@CYynkr?qbw1)Hc87 zYVw&cq5Z|F?bhUNNb0-5Nle2t_t_?Wv$;ff*FhKyIrftvx*}h43IYIzSr%7U{BPev z1Y>g7@k|wsOhOhTFfhjz# z?kO)w9)gkkKg$Z6RMVOWjS`RLtS>j~V2S^wqvr*qK70ff?%Yg=74*ZVIxQ2>=Eizj z{hLhY;Z7L>`yc^6P0yLfFD4ARg3T=5FR*q=_04r|pYqg(;>#opgR zv8L=QyTVs7_$nqZ9SRYM!;tM1oFFy}m*|QO3$i{5IICoN1-dgyH;?G*{u-!^XDGCC zDU+aFJwtVbVfqK8yz8Qn1hm#pIdZ47NpgbAPZbhOAq7G8rz;A()C~^kYr_U#@Fhm? z+`VRr^@8MrxTX~XiDnLg4BPzsioH*#&QBypEN@y~vh8-2bIXjs9CmINL$AF^AVFoA5ygZh-3jR`?2zBTrS!Q?X1>Q|kMSUrXxu+5Y#5&vXd z$H*M6uMzdn3C93ev`)CC2yuV~lkv~D9PPw^hG;t(Nl3UDidD$CgRJLR{^2_3aW%?l z?yn)q=42GHGw~0@zKR2~P)=@{cu+tq>~bFMGPq(d*Zk}92q3M~<(ni{b!ks3O z`e_9~V?(^Q4bpucsb57?&e$cJ_ukCpUZ@I-Du-U`Uyl|v##p$)?rq(iSN`62D)c)$u zT6{W@#k5wOBt7(5(;D!f0|^v6f(3)k3Oo&Tx0*jKWm?#d$91; zd4lL%&^V0>bp9&ivZ&m#sOj=e-$lhRtZ7Jh!ZCl{zq{XjU4Cp|JDNs6brbO->(LQ0 zPmF!=Be!EEBO&B5S3$g#uV)zg*G)u=b6o6pvExi#1H+p8x45S+t^88Z_o27g@Y($0%R7AttJJ@bQIEx~e`t&I z4HiQHYz%k&s6^@ePuq)+Nopj`t`=Z!Gj2A1Zhk#B_fG-gR<>nC+ZKo67;lQYAhb?tEm;)Wq_(1u5ul9lDw5(NO@a}ren)xEm z+l|}hoL82sDqwygJ?OdU+`*p>ITG@^Qqu_x2fOxlG_iU)_Y;NKCmh!?PdJa}I{r(4 z#hyBS&5Mh~ zFGz0gr{tSF?$>h#os z5}KC5uhEA}_&nV+O6bMe>XhX`^K%~jKaT_nH91S_(*o2obBzkNXwd;G{e0mslVo*5=_ci z7#XD^_hnovVf|i7Sm~jNV+J=30x}){t?KTK^V<~o{$yZm-~jGsUY`^poQ*mwR!uxK z(+34`%*HFLpLqCLI~`G@o#a@mURwKTVj!I^g7DCf!Ls|zo`cEDsDFW_{%$Qg7m-+g5FBUI;fT~g81Q~{ zYx?rUm7uv~j!LuQ)XL0p-`%V4_qv{;1e{(ww)@wIi`QSL7SQ_s{Q7eD>+9cN1u}d7 zEDo5x`SX3uchBD+&kwx$`*Zr(o`0(!uD|*BdvRpX+TYc$Z`RgeL=c8AflyXLm}~`z zGA-dq9~2@eL^^I$Nv2}65M?elk7G-?>Ptclo#-lCT#}`0HI4JDa8OX$X?VvF4Nd@@JOrvayRq<}MOj*( zDUHhWT1pNE+O^PPzDy*5K5o3W+uMLpXnx9E+iI!A%ZDlB+;jZIS$`FTBfr>HeA6s z*9zwJ?UbWdB^S*y?2cNm1Q&xsp}3XW)8$=ozLaB8QPJpe{jt--G3G+)r2rUSD&EN7 zd75+%a5KgP_A@lH5T!aZbR;$Ja2AEw$>zxBO&8hg)JQwegAYo1sxDwKDaXszK(vTK z7xIo4jC1feZ>g)d@vZIzxaxcx%hmQ@s$Jp1+k61+sEa2qSnn!3v_oZN<}4`n{SrAM`F(U)^cR=aIFi_~qX zW+VD2n=0JeWF%DGOI6&%QJebd1L79=4D4Xa;Ks@uXO+Mp%OHAX4yjAQqyMtXRwjP? zXdS`Qi{W&IybC``(W<`3M|yxS#MdiIj(iwp7aKtcA_qQ{lT;aw%O>3YfsJ#@v==8L zRMCz+bdSuJugX$=KD@!Hn#gB2?#yGr`TP2}@?0r2`mzy~GrZPw4IXkx-7|F* zZxLP3*O;!FPM;&lkCh~!vI_E)17Ooc2!_)8(Q?B{XWksy{!!S4i%u-PY-sfIz0I9E zJw9xD?DN!@q@VYv)ejsRIrIAPc?bq)wH8r_0W_N<86IKZKTRIb>%8;8m`&KKspE$% zS=M&3aOnBsFoiqbx}c>TiYBXJpl#8_{Jo@}KyblC{z? z*yhBQp1w?c@pPfdP^P+h3Sf1co;LH0{40w0PvTnjk-3C7Dz76jD>ttHHKt7~9|be1 z@t1&*(K;2)qJK-5HPgTB<@}lT>{z*T{r3akwO(IfGubi5jM~B)w0e2|tp1!zWnVf}8*iVu~qB zyUEhu1n>=z{KEA4Q7J&glwt6Jyy<1rd<2cJkS8d?cjEs|s|-g``dR9&Jhc^2C5?hX zPm@jwmCp&V_KWh2F{RDdaiE_{Gz|ZiqZqcRxHnE|zqxYU4aKN2B}pV;0|L}(^+K@! zb&Uj5phDxzynrb&#uU?!E3bWksfNX=?!2LVJx=-74b{D4iYdUU&uo$)_eQaV=Idz* zJV-o8L7=DAcr5&~tLZ-wxtsTvINVsW0lV+(vQn*l35AY-Ymgdw|_n@U9gVBH*H3|?)t|%#umEnrMmfYx?4AEABJI;^GP-FhYf@> zHvq|6p?@1pmH?usYNY=`8k>Q^K^{uZPxcsHxrrrJ1|XeBHRVI(@+sKCJh%?)fzeyZ ziG8=lO{yge%4>yW3^w~}i9qQOps593E2gW?(5bj5RM{=&Dz~UIx6Y8am`S<;pM@Gg z4LLxl*7Q-*nkOTIp==6tl&s2b&1K54@c> zA6g?l4?}rHtHc9ds;iD zEZjpTiR_?FkuY(;#upI8dBk|KgA)ZOqDzdQaC#;0{QiXVF_zOyd54x;s0%EO?)5Yc zdO>5cS_o4joaOT6iOWoa^QT)bZ=R@W_`9rrcQ&+g`IOKSdy8=~7q)S_+CV6Kg^o=U z0&4*Rm096+rkTbm0G6zC+?jRUiU;rniX^_+{=~+EfC68!n2vRW;wJbv`%5WelC-i0 zOEr=R7b<5?qa5k#+dw$4M&%Sw;a#v48&D|?#(7N>+7aI7gAr{CK0W^H%XvQcm%O{m zBzg~Sd>$+n4iSBVJ(O|CO+vBRC|^op3@>e*Dcgv!-Z-G3IW)deI&q_H%f_)XiF3U) zr-Pf1>-+s#Pp6Spd;}$-I+`Da*NcVNDuE(P;dtsT&6@&`J9$1ggE7?U(~Y4D#0ZJr zU}YOWN_wZtABy@%ArenlhlG!-rj=+c$%7}izj}x)tJ)c2B<{Swbw^9fj=PFGrcdr@&)K|~ zn0mXY8glcKd+#QNnRbiN(UJAj0XH);;k*Wxtj!}icwXMe;D?)Ibdo4!fO0LILQo2M zb2G$xVz0K%9`~fQ+rz3KIhx;KVT&B;g}BgN%VDeQaW{cbFkI4$6%uR{;b7&LvW>Xt z7s0WK%ub5Tza3e0`vUPHc;5@&%vbpp{IiTK{i4Qa5VqY7RZzAp7d^gCaSX5*Dpv~R z7@a`tR?U_#DlWlNb=EI3&=B7Ft#bqCbIsW!7hw!QE@0mZP6)L}Wa+6I>^4YV6fA|d@la7lyR!Dj`Nn+atF6#g zYUOoG53cv-s2>fX_zOv10=PtQ!XXg#&0Q+%picoW@kkD_vPmzU#Z23E>>$ja6%%k__uNy(3@{AF-qg|0t5NA$I(*Q*!j$*Ok$_2i9 zbRz1NKlvC7+t6ATn~&D_s*xAKM~f%EG<8CGAS) zm<4mx0OF4-S~uFie$yK3W0oLvjFSER|^&3rZ}S_ z!I1s&yrTb)tb6}!`Tyg`|J?JrV>@;3tDF5e%%|G<9P_P9Ks_s8viy)kuv%k>_x)z>*0 ztMVInTX1bzB{wQrtGRn`(6m^Ug11>a_fFRRFg<5isxGTGm;`{w@TBS)hg48EGhO!U z(T{+`C+XpKPXoZmu=|2MLYFCVDu%MF+H|Pgr~`plS?g$1<9{)RGoxCt6RC`){uMzA z-cQ`&NL}&4_`KMveE&gR>A_#Xp$*ye&8w(|Y|Jjc78TIGY$|x)QhC}cT>oQJ9qrOW zF+hSfm0x2>HbY=N&3FXH?$SA8gulFyT5h1@{(c$w!^PA>4}baokB!Bo-%|VSEHGFI z#EXq>s);{L(M(o^17D{}oN`pE9e8EMQNH$!8xjlX&*`Cn)#5P(9{aoKg~TwJ1qzN- zmfom2xXJp$$D5v?K^hCVII7hT*aX6dyXT*v6x^&&q_Jf*?IYA833QSbGYG7; zDMnvzkYuVl+tQiuJ({?9)m2fb993_sb%<-cn&VNeKpvuC4)4%9`ukk<2A{xDt>OB~ z7B*JWUvvAp3cTp>D?xgi;*)6ev_}axJn$7)?CyFfie0~04wbU8L1N6d6+&ecLa&PJ zH6<~r_-wO3W|m=kHoNin`tHKo7s&%9eVj;~pxRi1(_IH7=ZKvfVszF4Q?Uj|FY`e$-(=Xv zV2eER-l<|L=01;q+SU#a?t`@0#{1=u>qm8hVEwObz0VRFQ;vI8W;|I4p4$28^YmAq zq!qHiZIyic;ftwVw^BKLgC8?jr(bN~E*)G5f=o$zRA7X4JfyKHt9cs~OB`kS%QfV$ zN!tdgXMs6eq~i~o-8OSN7o@@F>m=z+IapJg!s<*_iW<;%p5sYn^3c|IguqTq?wjD7 zsYzCMH_s*a*v1hze!PfyOtUpvvBHLyAD5WZLoN!8V4*EmrgpO+xT@#)+`Su?;lVyJ z3+A#6!TU|oi|H)fwnN;5ECEUgRfsV?whz2My!uLPyX9`_{TZ=(%}NzR{_3{{ST^)Z ze&l(z&H3oX8w1fvH;-@c<83_G&klTCa$$Z%Z0GJDZ!IM0^oy@4q0Eq){-0a>`hvi9 z5=l_a>wm=CK^G$YYx>sQa-srHT>d^a&N0{eJkU2+Biwf5cF*ImJV-FAQxtf5OjWG6 zR#V0{*uf?iNN>1e3P|m&=&kqu?_TOMX|gr5?&6D-gdN*zqE~= z6bH^cgEd}p@g zksBz@km^L81$Cmweb2r`z`XPrL*?%p%!**5+Q*+hyUcSM(l&0*WZVHA<=g#g2A-a< zbKNxRgyC++0)}Th7FPjkAw%5m{h;{M>wm=^KjnWM%5lcotbzJwuCZ1%S_Bw5nj$^r zIxM|PJJ!vQ{KR!)jA-;>uT+Bv;NCeL?Uf(-JgVj=+^pGWY}0@#8hvQpH-3<-df)W& z+q9Gp+I{xBZ=sr1#GAB?VngM`sd1BQ>~p+Kr)ZgmxA(-cE9S;B%9aCAjy~?p`48G= zsA>b#C=O;l1HGvcjWDC2)Mzvr&$SSHBr!A-$ih0*bnF30t<}QVy#49rEc*GM6JrP$j3R#L)$73G*4CQhwXpoaXJp^Ec)pfPRB)VD5_L< zGj!@4>uQI3a&CzCGMnCXmKb>KBDh^1zgMwf&7BgrEARTMJQke-dLs(d^rAx)&KI#) z*4e9WXtpPW=v%6_dM${<)Qd6;uJxSXgc6okz(sJYQV=I6Y}H! zI=4-j8~vJ#9(EJHQ0|tb`HX{rTyfJ+QkfvH@&1=iSW}Di3s!93f4g*rRM=CGQPgu-!+FmwuF>lb*01pA+`DhQd1OL zs_9u3q-J8*(ujaX4vNBj`~Muvfar36a>>A007;GIR6nYrFRKx8y-2X``wK z1|ut1k_i2|Sp`_oBChw!7M>XCk&^!NWvY6wBJX&7PMSXnSOx%)QR8w9G?4TJjiqs9 zkVF536v9kCvVMlj%o;-;#8BG9y#ENEsP35EbVoNC%6#x7HYY=(>0=n6t~bUr4Wsqi zm|wJ;K2>W2YmPrEqqSd9a)*7F5u#&|`4F|K9i>Z1|3u7lSjg9rJcUP5{C|&ppj{fj zGe>l3_xmtbFz<@{0#Po(+Nk46ko>d7{(M9mHQ&+y+MDwi5VETKybotimMM2kp1;(8 z^L_V3UMDpeR>gbcC!SnIrZcgoK&qnsp|>(=*w;Yb_DnRQDAHvKtt-!-u?g#Rv?8RA)1<+oD84 zIc8dIBySh_{T}~ssX9k}w0vKg+*8A*#_$hM=uJah?c2j;TfGWC+5~gRo z=tMP~O*oHuUsna3TIi>C$1d}VIpG6X)Z=HJT3(W5QLmc0E~i$byY|Bxtp6k4ULZr%#o*JjzhpW0>S_$DW5@k1 z@+X>3c~B92jgeYXkCOyqmXo~(fG{#w)gKSo2ZCI+F}i5Sym!Sae*$sWuLH-Usgo#> zW^b(;rbPyIsIujo&-c++i^1fU8lyu!>{lavp>rc`?X@3@g26*w-UzF)=QwiMNiRVPl}` zc@M7kP;Xl;NB_hgUE!yhTrzfTIEIg8ah=t~(emHJ>~k60%tRFymgG&==QpQai2ihb z<=yk{vYa=Ni3;pgqm^vJN-6KfdFuN|OPL)sLimIV>2 z!1XAnz+rYd{xjW=+Ql7OqiPKOHPNmveyX3L(19zh>#KuO4=z4;4dbstEDv-)s$bezQmRZ zkud#7fdCvt33xGa#h?ujtdOq zJPS1yD6?403#;$|@r^qRLLx65yj1emCNbMV%*F*@W(zY*s!?h!U5?wti+0RFCWl3U z#!*CTs-LOVn+c3p!1N+YrCfim=4l&vc|H?4#|NKG|Rb1|2x`a0TXj}TJ`N!`uP&U{7_gOHq#K1pjqV$b8(PsMg7VRN*5B!B3Jkwyfu1 z@y}NK>Ub2?{@`y9x+45*MUo)vf#;VS4|f~0u$=gq75Ts&5d@6e5CF{yxb~6Kt-K7u z+NP%2G%!Ck<9^R4&~TX5efeYLl3n@QZk){6fZ2ivE*0mYYyxV0UhH{}*M~z*tDi2z z{z%VU>%iOR@v{7%=+1(TwA@WFl51h!Nwe~Sb?0Ia?b54;a=%twYJJ<_i6AGez;N!i zP6p5mGfLW~ipl}qL21$Do$Ja8s)FL9oc#VvyM8oZ`ricB|J8=f(rN(n`M}P>+w*5T z?C(tP+c!79oHr>7EBSKv;@rfN-M7swR5$Geq!nlk7Y1M{KV zI!sQi5km*3k>9wuZ-;m5)$t8__^Z1R_u#}?9t!B#z}2GO4n#OhHvM72(~f%oeu6u@ z5y$1kZ88%TpO3CXj9jOp6-exL@_hhx`8xH;5GjQhdZ7*2_ZaU5Lk&N*{=noY24qe3 zL?-Z9bU|1h?h_Z)G(<(i$bK>T;X<}nlRz#fup2XtCJK|W!jzy$0~%m0BV~vnTS{OF z8!?ocmlFhf{(u&9ac*evfGOhnN`x=8xQPdO(p=wMFTzsQ$vom+5ppq%b(i~;Iv~us zp4>WX0t5e24pg1BSCfDmrwpkvz>))8j6#mywh#vXF=Q&Dq?*ghpnyzP;NAWRt;cz?g;q zGrZ3Oqn)uS=850FwxzF){MqbcL>`d1IZ~#_X4!% z$w?bI5E&*NmMhT!3J4dGiU81ni|vQi`nkxhJtc|v4sF#_UC5%Jwgr|eplTL5P7Dok z)fOtCBsqwYAvXH1aOXk$w>urqcoxHv6K{%;IgM-Fu%s;_ zVoN^->v09dp#c)gCd^iq3 zy=LKA6znVoV=u@dgbnWRO-#2oGVv!hySKkFsRj9x?8` zxZ-_D${r{OiQ6o2yT>I)au8hX;(EY~Ji1VDgi03UCV0d=5yEF79b^$N$}yir5&!AF zTvWni(-r1kuNHyD^l)~iOU z@Lqrw!D~jK8$G;b`t=dmeirzafhm^|c@okB0AaUU?SQj76rTb}0`tF8G-#*fGK|(U z$S3&JBTYQxKsrl3%-E5%nM=-B;2D}OQ#4W(3%OhH9~V3z2b*t*4{kYk`214d{KLLK zA`ZrZXLt@mICOo7+U6tarh(e#TEO58X0cxNvsxvgoD-;&k#7T-?;QKgn$repwWody zZ#_435@p_)%t0wa9z{Uk($QBaL~U+O7)phak{Duj83_Ex!qOONGo-$0u!%=d(Zd!1 zDQ=Q*`8m0L`Gq8F^+dqoz79QA4KR;b`gA^{OKs_6XXguJ>IOs}|jhp!Yx{h1`#R*n9m>Ujf z9RAp5PiQZZB6=u!D;3&83g|iqzIsQVmg9W@zyL=2gp+GI&`}xDo+Su5gKTTuG7aP1 zDfADtg0p~q7(o23Ab){T6J~^Ok}WkH=uWrVcUE%0DdD;tj!)`3tN_Cs7b!(#jo};w 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

(<6OA>cS{P$fgH)<5ot){8?-eymhq` zlq_Qg2_vf4f25uT`QNNE5yMGcleN8(f0)j^N~ez=^NJwgY+*_J($bm}U>8t>wR#Yd z`|?zo_#)9vu$f=jpkyWn5dgz{L&-$`77)yi}s4f+LUaq((5y$CtSD zr-;Fw|C7JHUgV^!j0mX0 ze|HF{sEAIOzG=~>>#S7Mzg?agRmPGtwd#sd>g6B)Kf1Ggc_=W*6;SZvQ7d3Vwh>T zNdo~a=9Vb6)?f)kK<~g}&WY+z4UqpjO`T+=@_i0Sv@W};aT&V=^&GIlxjhrKS|r(k zl>^Cv9v(9Z$)NvlddLG{`fVdB&^N+(+&Lk6`J!1@y|J`MfpSmVw$$t$@f5cZi>AtV z#ai1>vxw`=2M!w_=?Or#})qt-TUT8pih228u3t0z5 zlpXOl^ln_j8niZ^!md3S!h0^d7Be+%%~W9a46exQN@nuw{aTtd?9qH1<>~kvR%aT( zcNuiS@9C)lxw0@EDM?=yzsgOapguD_9}RN4BczIpaC#|9VV)TNe5R>BOWwG9hbHNn z$R~Q%ZTdG!v{I{MP=G{x6hxrjh!0r-=n=(t_n!L#N+oM3RAY-)gnA4|6HZO^`pcl6 zjkG`-uZxN|5a4KSmiX(i9)avrkrk$rU$^ooYN~Z!0x_G4H#?}7dlRX|Aar%(=ss1Rr%-tv6`s zMGTz%K;wC)T1=Q$YMHVkqbedBaUJtjk)k{CS}DQeeg=CCd2dIlBGI6BndhKOYPt)_ z@#W@;Qu~XNWFmYmNcl!B_X`3l&zvjRoOGCA&&2|E9tUK5i<8NR6t@)+Ecwm>o(~wF zIm|&>nXE5M+}azClOgdloDs~wmSyVmvL+QW)!)y_{KgE+v7iH7DiKTkrP%`a(!qX5 z&9Myd1QAE7l;tezHi4+K02N0~Ay+=0v{EU2l9W1?|LOlhUfRomm29*rM&-_`)P}b> zgM2M=m-mmk$oU;5IU=%NTG-YOM0g~n%?m$~#iu~A@`UjBv804c9g`N_-Z+Aiys;q- z%0s(2!yJhMWrrdNJoP_SkK1E8)S10&WKae`5J`w+AQwQfHc_!Ri?kg+-HVZ)8b%f| z-yZBb#efvrm!0@Acmd*2ka_-BS%t_hA#K(>$4r1F5kfu=`sqX>pO*>-8Lj(3YN^8g z6dpk;1VsVr9R@2ip`lEY^I;kNH?n6Ku;*aKAUq_Ov}Is6?MK!qQQw$K8kAdiSn`Ff zLF9U~y*JEo9Fa5 z11xb;<&l6b$xwggCZ^|uR!7F*{;%%wpQw_{{*0z|NlnU(5mMf zDDtA2<|pJ)`XpLLJGfn_IyZEiYf?o!MF``4Y0dE?S4qOn~Y z%r1Y5H4hg+Y~Bnb?~F8}NY=6tH^@FW@Ynz|L=p3{5d|%I35K~S+K+$olL((?mtq=; z!t0B0qX&3B%C~I>NSOw*#bbTCkQrpzTs*fCaqTV{7BgW}jISLkwMtw&mb;Wn>cdB& zh#?t5Elob^^5=BcHg7s@%@c7O3|t6Hgj5P)Qq44-iv$5;DI92!i2QcQ&#%f)_=#DL zJY4X$lY|ga_eUvm&Ra!Go*wx+pb zKNlsE@P{MKByfIUyiJ7S$6M2sU>)Kn`8GH*GAOdt@d`12Ti)!o?0P*B0>O)B0J$pG zbf>7zn@Nfg7T+7`d1ey0=<;DFbHS&Is1FZeOI4h`a>T(JW0TDkFJe^m2M=yM8Jz0B zz_JdtR6qTMh*M>Vs!L;S&E69f0F5piBTkTvH$JIdqUev+jz?T_xK>zc2IRv*N1@(PV z^e_?hT`evah$bra-*iEF5p4!JJd?E8!e`0RH7Exh=3fK&NQ--s&sN-#*dZUfy#%r* z3VfCB3Zo3l^q`qt1wVlpCij^ulqlv$so#sDo@4k;7sMV4uo8K8OHevNAtsJQn`hw2 zc-~s7sQKbT<4$n18DzE>dpA?5f*xD@@v0F!#G_;pMsU4kHW{flcoH5dvEj6p@`Pf4 z85e>F2Z2DKuFkK?k`qMaS}CW|eKKXj;x-Kw*~K~RfVuUEb}R~Pck#>Nd1KU5v?;s2 zpD1r&ycJQ=kyK&%x0>AR&^106j+h(b?eUHh@{Zz_M+3U^r$5jUj$*-jUrAtrAUl@i z2J2X@aKaZ@3Lc<77m54M-}#Da!}WAfFqMi!&?a2D-ztzkbXqk!WgV(R+5OEx_7jtyu#CIOsFXF4bSC^2 z3);ZKxs`H(7K6$t(Ob3>zuC2XTLAjkWPoamXVg)V<9T6gKQ4P%lbi3-dP&}DS(-} zeQD-k3~EgTy7?5%9L}DLL9Jw5pP``p8cZLY%*V2T&gWbG0G8!$U~S?MikLA_O?su6 zxtG2&PrPk6A!S<%87dX|sw#EB;w)Q)n5BrYe10`QOaK$K#>|S$r@scYhn>#5^1<|E zenFx2Exn`Wxo@8T^p>(TOR}a2?NcSK%{cE?qQ9w9L#2-g7(FB2Qm+2foXtLqrK>imzi%*W^47qcm;n%Y`c8v>EsXN*>PM*G&|i9av*cz(a|`NNs#kHp)WJldWXwzbW)y%2Bj z@o0Zp*gibd{#v}_tw+cE!j74ljs@|~WslB}g`J;gI=_g&_~!BASK*7lGcVZUOmNYO z$q2&%8ma?>;^WtC{Ep>l)#BFT%6!;Ir_2hx}Zb& zh;R{w_HNfWGkGG2&?Rvg$So+%^ZlZlF2&Gu$uNj0_5O|2$rB_D4e!sy8@5Nwvzdxf zIGH@$fC}Y=7l<>LInn9Md>;l&x(P^qqsV^DhMfo7N09tjQsA!!2{Mi`aJ4f`4*H^C zqpkQQTT!V}rYljo=T_eroNjL@j>~NH&SboK zYt{kfi6_*&COjOIS^;ud`j|YkLL>>8=fBk-g&MrK8ehEt&a&#-o&9jkg0j;EU$F#n zF!7R7)Qu+IB!&dao4Z6CW(k?exWmEn1+Y|YO+aX22g@U{aktUdE<2sWY3 z`JbE-$wYGro;aW4hjTghOwhrYVs%YEK#nLdzlbg)&Z+nvciI0CT%v_L8N5K`d(aF8 z0Yt+}lXDPVaTYQ?u(dR&30nnCZEU7f>m(u6k2OvUJXR5c$Oqa|3mIuVk@WxypwU+TG zS_vQKE$1ac_EnZ{%ODd+E?cHt>dczLf+n`gW!3yGs#MCItrK~b9P$mz3-kVV=BJdU zNECm{N<@}a3H^0|!~B&?K|YJv2c6KB@81_bd#tGl=SuT;dr#{Se+C1%tL}?$P>w&4 zkK5DP3cZJ40TeYMGiA*D0)HOcIn0fuU3#bhUilIFtwrkQ0zjK8-XeunDg65So)#@x z0t2-1F%w}CB}fcV5C{@N%=+fWGXRi?BOeF=@S&|Sk)s1>0Kk6aI_Ff=z*ZkU_6)w< zhf2Z%?ROZCnP^;Zz~$8U)mDjDuDN5hQyaRTS*oE)pIttdWQo|MK7QcT!vs72d9$PG zFAYrM5^^jWKv2NtG`McIsK2ZQs5~yj5o>nbfrpJdJIN`Q0s<0|yx7HHt>xr;H^Wd{ zU)S#e$)LNf=VO2%S-Q5I9znvswPCR*RY1jSr1kOgOHSQ=iT$(2)!}@t;Q5hfO&?z~ zEIg{1X73D4RPui)J8kC)O8MWw1>T3Oh@Kq0FSBnL{ai2i6-107joF-T56Efllf0&@mYN7->q}nFGFi%a zwi^ApqKVfy1se|LncSDnIwYC_7&T>&Epzdz4-iL8*zFd5u)lwr?*otHdU$WSjqD`r z?lYQ>i%TIHT&90hWh@DM>lRrWqG18uM{3jgKULg55l-~_QyIe)-^x7!<~5anxY;wEX%++(ySOtaHCME?Ygp7Yu5G=DbU z0(nR#J1GFE@~tDhwhy2h0;#)jbqjkdZ`x|=&a7#r2yUi3vyPgUkK$WdZPdC2~3$$^68_|9r@@-V_W9_$BM!z<{y(YrdzK>aoTzHZN z7Qp~cDEP*He@n7a`!V6nwx{xUO*HXpks40_cu%G~6aa1Y%8wgd#)*r5@GGy=IT>=V zSO+aH*OR4x^$C;sA>x|LuemE<=6=pzTdvbx2z`6u=_2h{ogRzMyZvj4D(k4Xnq%k{ zGyl&-l?vekl=Hm2*|(+B80KN3zmloYd1bBs-2C2Z?ZX4T`sMdVLvS><-4g{w_}#{XW<%NqZ) zbPjmputRp^7s!}iV&~spriQ7|_nH4{pbF3a+lDj${m7#H;l{IJzh6MavZ>1|48T7T zGx#<)5#4oSmL=vY|ZxTb|?zYc$5mKWXK1|^_~Fb z5}tq|M;4JpjFezTHvEKyEJAmOLSQu{c{PB!-U##)4mSXADb-$e8z~!?Bjb!b@m!yX zNvJjN6fq-!L^;OHyNY$=%nE}bA7&%^ItHoR3g!g>%CUn1){&kvT;Cf&i2t+zfV9-5 z@3Q#HFrc|uqvQoP0w^K!VZa;&ym`}E&KUb?;Fxv9&BEFhS<{-Mj_&-o7}~3fT7O=W z48C4?wX`Jh0^83$;Df0hfPI>r>euC$zmdkNN&klJ*MtdV(>CQ~{!ZDs#NND}yD6u9 zOlio!s`2jI5s2`#bP6|4S|W}`0tk}Q!-zn5wOf)(s#%h_ZxZwer(<^Zf0b78ob_Py z!0UuPdk`o3YS75I%NtiJ84_b7d1LYVnZteD2i4ZxtRvXNMY5$=DAwwhqrBdVzMX(=Y)b+TaNNtI~4lB56glZ9OG z?rX1pVw0{taIUcCb?4+Y%&zWIxk(hjgRvBZq8&EXF8876b(MCA{peHa>tW`i7herqo z;Q?fnEQnAIpzjZUc(uI==nkOk)f1CyD!L>SfA<~sBx2E=k_BTzMxfI}cRtD;i6y3a z;LGsoiJvsyO+=!R@a}aFmYGBdHk6QXHA@-wwDqOKw#=PEp^yCUTL)a!FV%6+Z8}D` zz3!`zteKhJv36~HcjQs@;P9LW?zlTcNG)(U0^s6nkvScEA%t6NWI}xVA%S5~|*->aviK5xS{k4zSX5RPZCCTU>Vx>5Jf!1~q`M zs&mj#NDnp$^3dPk{#@SC3aJ+ab4(R2xsBK(A_G`#l^d%|zft}Y@;lseopLU5M-Ote z#VUmq2IQR62&wjga!aMp$B{k^x!s`ODK}bGKC3_MvHdmgwd$I7p6Ez`d{f>iZcX#j zpV5$(rn{fw!2CL&`}Kc1#(xplkU7gIZB$RZ8UJ`i{tIg`!s}A;oRPoYQ~fuQH@+3C z6#Ls9bAQ{3q3NP>C{UyiOaYq@=+xx85zZ9sCCZp@HlT@-t$p2;@XYAZVD@b%B6L0s zYOE>^5+F80xya2L@JpPWIPgVc+Bxxv>`kW?25_Ol$_R6QQ>T>(oX1#+&Uz;c=ZWcs_q zIGi5o22fDu0iuU)hB#%o2|d1ZU3iC@S_LG7IYIY~^Ru6;49~a=jzI}{4Du?;3^*}h zElQBSb6%?teGmf+MwU~210)1Kwwj|W94|kVVs3hzwYl?O_tF5HCSm$t`4Zo+XYc=Q zUs<@ikV%YgTR!t8?&I?X9;x5G!^Jz-OxsyPyjS{V6Lyl}`>RU#uCyCgFNQqYtMp<3 z{&thW_V+GNy)?;hyl1=>eDC@CLc-6N(Ve^bSw`FXkN%F%bpE=)ha2I!8$wKytd#~)3nfCZV?4s@^K85A~? zf<{mgdQ|Qy3h&f4QCq6;6qPTZA|6APRHWhusMu5*`;a0{%8mBMCjr!&@RSK`Wbx{) z^Oo(nC3;``?TY;yO#|p9LINneFKI&6GzDAQ(EyrkHPvCAW{ah%3?wj$iV1L~f{*Ozr{@deNojfcX`IseqV+T+ zCGD0h?)9gijHTb-m<+u^gF%H?K=B;>h(sw^mlrhPY2S6s*l6S09QVcdYEurKJYFS z1j7Jaoq*CTcO;SXbrBREs6fex&d=Hlkge90{nwgRvYVwTldW|r0B|K6QIO5`EF1M9 z8~rC6BbvjnpCfoWNBBw(R$u#G51on?oz=t6+NPhC1`4v$=k#tK3cN0p29^uFsq*2b z^0RcsPd5(_-c-7hi6q=iw*oY+179+ZeAoe@J`%(SZ+%+N`H-KEFUVC0yrq3bNK5n< ziF4yvaLYOEmQ&ho*P6_e$Xo6OIpmrw0S?y5x<+9fm#l#(R7>G&?(kJhDGl5VRk{{- zn#NDaiznqd#N-9*gTn{&*cU(LMWyA%22v?U^JvfVd;{<7^#YmzXe$=jkD@6gYqXn( z#OB_K|8OV%PhL_%e#)PMk`D!mNAF%$x?3rlUtV*UfW1@yEci-3pc)QDj)6y5@Y6)@ zWkytu$PH^(B#BgmQ>Yi)M*Jh8})0Z!KakAlm4S#*RL9SA5^Arwcr7QYHB z-pnqh*%VKv6;G!@XFnCQ2BVig6t9Su%;=Xa1QsSVKpzv(BtgLQF>VA?+p$OWv3?3Yq%Y?wZRZfkhZf5r$DT@~P-Dy!g$hk~QSLPtS_J43;dMF8TPO zWaCfix>6Y~y^M{YF8!QVa_`q&!q90?LEdyG(20V+4gd%OI47*&Z34^^p9&yfYs|hT zI(}i`Cr=p@_ML$MG(gue2*~}4b7Sa1NEpYEGkzJGm{jR*SGh7-c_^>aL#%4sx{4fI zd2X+=j9KYFR2AxOTQg98YN!00^YMQTyno~>1`n4Xi7ls62mlg@1Eo5$E{mE~Sf}&Z zPG7R$s30jjfr;p&iJlIw7PAGS3HBY}5|#Yf3fN4EC7`o#VuRm;sNULtgBltO2#%cC{z0c^wF~ zLu78Y>bu&v2JkWEx(PAf$-Ua~joNn)>ZbPU-ip<~GN_*ps(*jCjx|)bG*mydqi6&m zU^3HKAplS&a%}-Bv{fKF+<=phpBT3_5@$ec z^WkQv>rd>sTV4J>N&4KJa_mWRTXXvL){L2E_p8lx@n>YiXD19>`cfP3a_jJy^6F25 z?j^#v2}o;F0P-Z(yEF<;LGiqMrhf-~mW6(B9rZuLPS57#{{}#+&225$+iZ4DGH2R) z9=7qBwRL@N+a~t;r1_A+h2%x46D2tJ@%rXyW^os$IkT^udlcN^?0%Ou>H?4SA5Ge1WHF9 z6S74{_KsV1ma2I+oJ}$Ve?JvP*g z8h&atTpu$0uxR+n{*W_fxU+q@)10Rs0~BKctT~P|5yd{HtCr|FKrb4QZ9p8uSpFfw zAl!hQqS%io2lI*s3yTIyP(x+*LoY^#yK0AfLq@u1U-iunf6Ey8ih9k=7~b1_wQn@? zH*TbNR>NF)0LG(DC>#(R1HTI6KGcx%bA|WflzYgEb+WYH0Yyh446y-#Y`h=CH9%iw zk6&hj)8F#0WvgQ|b>z?JD%QO@Z}CR07^NYhe>nb)(uKFGnQsZkZw$BI=y$w1H2PL1 zjGs?!oG&N%kH#VV=FUu&Ty-Y{7ccGY7uGf`;nt{+mU2s{Ri#H_CpN z(Pi?fCq&-T;;^p;f3Lan#G=~wMXeQcwoHPlUWJ8`^OD^5qHwQ9=|9yT5L%cF#t@pa zsf#P+i!1>PUhO4~S4(EwOGh9pR+3AmH&&EZmPG`Yc@I`fq>p)3*!=TByyDQ|VLnOJ zcUTeos3eU$o9QR&{=1@SQEUf`IU-)@*P-kw?9O zv0r+&vCrelkF!s2d$hgtfWkbxe~<-Ec?oBEVb6N=wCpZb?r!ywyTZIhTf9Xn@MZiN zfqQ6aUT?uQFT@ws14s93rO2lZ=vOR=2kJD}>$9jYX94nLzT+OSQ@gm=UI^INqYJxg z*LEEqeHVTG{aDub_dmS$6Mhr}1FW;b$9H&w=*U49NcaY3ReZ|(vTu5ouRA-yW-YKF zDzNZpVAu=)J5~NMI)VA8ez84-gUU|@y#>~~p8|NEf_tTy72o;g8pRxM{3-WGUzzay z^qHTqf`IFj0aRFEIQwVR>%f?Me`2$K7a#vc|MI6?W3Tkuuk@@R53Y%#6+%+Wkou{K zvt)>TPspp531m+AL{-RC)rDEuQE7!xm0uBRy8Dp(5pvP{nlB@edHY|iFX`m9mE=|w zOxMWk?$>gu)$xYo8i1HEAP8{r^o8(NjSFoTFLZnf$HPOrzw8q}Mhx+?-+wviJI)?8 z4f~eG{*v|YPsbhrNMsRdP##IkeA1$M3Xg-|8^%Iwsn*I&kr^*PZ=m-PI`RF(=O*1>+w&SNq`&Zg z99dv$RHECgl+*Az)5iWQnb01!J>PXb`qe>P?C+J)Hdm<^``67D4pzTe(f3w*E;7GP zTky6|uRp(FFdzB}csG{4|5!l)LnN-Zu3$Lx%}+1$&Cxb#W2RFZct;f^+);47xa_cBkaf-toe(l|1C_nO4chWEvx;3Kq z+Ws5KT%$6QbnNJy!1&Qi#X_#9FG6!&$(qeaoP1^`v`?SUJKu8J|6bjNL>}Q>VfWxi z#doGd9<8=`Tx7q1>UxI1JoMD_(rYW-Gne0=e|jc*Az9b!%E!v5URS^L>7I@Mwfgkz zH8xa_H7$pUgh|SZwK&uG=bKUAn4yk$$MoZRxr!I?yL0ek*1R1W zjc1!jnCYkS>!f;T9z#d1Wd+Os@$)fW`|X?Kuy)1wUf}#6GBwNEATYu>A1ZN~kx!LD z=8o}>=GVycj!N9Gk4>q2oZ=X~-}-9$^W&~zpX-17GKZ`D?rh zF{crXCJ(*`bNfvbf8v zfo$@+51gj?3USOfTVOIfSv{!~1vaw~GS{TrulNX+BL-As!>=a`%1YN%TUn)qXS;b; zNQ)~CXb96X1bThtk9@$OQA;=na=%u@7$vsioYWsg9}g=4W4LdTi;(UyXdUA*QS1@X z-&x^L*hR_qM%-zB(uZ3ghqx>E2#2M<)W}q__W=nM458Kt*RPEDI^HUH8&`Ft^3zBV z=-b`dq&3~l0*8yCCq?U7K;HUygU3_&Z@^=~CfyCiU9Azfp}XfbFBS}+`FVo#7rmy} z_RRj$39TaNAq6vV5Q*Y*i81aOU@pBemIxCp-k9~ZKJvf`xGp2P#uyZw&Y={8TL<5}7sz^<$vJMHW@sTTrPfCuo%FlE_n5qCvt?1_Rw( zZPS-{!hguQl>c4z)6|U|E}99eH?iF39cm-n+(wG7-J?ndUIA zH^PV&KsUL+K=4%d?H0j1n#B(s!XKP0*%PcIcLbeiPM@k*Z>jUStm-O#b?RmP?b@5o zA?~9Fr$(O()r&(f1jxn!4Az%LtOL3PH#AdlIF?vz^=C6hJP_jvkUr}McR>F~K2V-i z9Wh6Dve0ufJ;Di08;I9`IdV`zpK)kJ-puUBmdXDcm==rkuKPqkXU}ZaM>c!R%~|WV zO{~^m9{qdftCws0OL89%A*gPlM5l#E&qmhgdiICOpDoXtLoa?7pIuD()y{Hx6#cSr z{;M*!1LmiZICquxvp%{D(-}rv5MTI{KG`D$%SW~^rboHSz~#eUO1WRnJXsNm$FayjkWcD#v(v4+4+g$S7ngD319iLYkkG9_6(9t zB5vl*t|-`C8S-d}$ZN7cFHaX8=IqFCKiXW?&bu;ty57vQ?vM|EVT`-+T#Y$d^<2_e z>yZ9@cyT<+S6ZN8)OYP`!HZfy&2rIkKNIczh0TDYy0PP-E$qlbfzdU+#HqLU`rkw3 zceu}IGX>UDyAajm8A2>+saOQayq%R5^n?-#)@MGrj^T>4^oG4a^m7}P-Vm|{h>g0L zE3Ih4aClkKWB5MfY9_@+gZkbr;Ad00@mA1T!=x(tNSflgNV0br&zVp7}QYp7W}IoS7MN zUaN5aW7MyX(YerwW$|T+h1QP6GZ!O+jph$Y{dxIuD?I+2_=E;szfC?H?sJj>f7J>_ zHgXQNlU89HS_t@m+mTi`Jc>xu+RwRRP{hJ)0J=ljVXvraip8Idm{jD=ao$UaGN|p- zSc!)E%k@_!Hjjy1oqRg~p!Sa3dr@k})3g`vd8i5Jj!yI#>RUQ6tRbB-JIHGYl4O}@v$|1Er* z|MTYl{I`c9MvLc6u1*&YwiHDT>_md%k}cCgo$$_``x@CBFWmNC-aq*E`qBOm57U3kuRHgGO|R{} zUpV;n^4h_V7uODcEwI^eCa2IFD8~e8Gr^X`C~r1^7XxxW%7iB|Ik6tUUCI?b2)u}i z%3$7ZOi3WqZ3!U#>j~T&6<16 z#C#2K`9NzDJe~mmH+=&Nf&1GkU~|o+BF!X2%q5J>6ggF(MdtEFX7K_dZ$R*b*5`t8 zsY1181i>Da_AbRI-3qjnDQrpQmVASp2a0n3nO%j3B^ zBta`Xxjx7JUJ}kqaSVDGK=Wv2(^c|sp(!a*By(BCzEk#A4x4=^BduI!El&ydpY^ml zSpl*@i3sVzPc%@>nRHEn1)XW>(r$T1wx3)9a>n)fI9p$wwLZ7m?+vkz5PW$FWpl~i z`s}`S=y%E`+oY*70Qxsg0YJNO&f0RtX8IvMnwiL*N{b{yg!18rcasLipO;x61ho>y zVmRCjL>FKSz}i}7Q?JAfaDve?Gcx!AdSEa1Y|zIf<#v(Kj4i41Wmz|$&iZ- zVi|wMR_?;L}e=x4K8C zW=E&n9o`tF%r~kyBj_A;L6u0C!9#=efTr2bIJ}K5;3EnjYcOtM<;9H+eHq$vyOYH<69Z(xkbX5c&Y*x!l5EI@zc2ro}vTM#_F6(pla(HNk| z?a)avvIDm$+N`EA99_GCZuT2m_*^9l16Rd^^2?mWFFYV*(?@+0rFl{q&*{q8H}%SI ztn^Np*uFU=^iW<1R>*(E$c=8wqPE>k#xaw8#{gEgbouNz)j@9*GEc}HIU(nA;>hI_ zI=4?q##rw0r|_vcA@!QCVd3%Q0mjvF?Csa0b>refRNUOSq>wXSNLl*wN$m?K<j~~DJ(=vzB@t1fW}>f)ZT2+-%bO0ci1WB zwiIFj{V2d7F~4r%uZn6l07Oagh;VUBR#HU($vpCLL@GCc>PLq99zNw;3_gGUlz;N% zx#SezGn0PtlQF52WFhJdU?4J!k!k$FIo+^&2K-mY7!>~KMOtOU76@AG2+vADlXIYV!T^y=G4_nx8U1T(Q~IV zn%!bnr_z47U4?$gybMJYzXYd3FJP#f9!$ySa;K6%WRy{e<#6+TT0sC1$Wp6wR=VT< zA(8l4hunA(o9wg)^GJms8=x3OP{jq3597-OC>3Jej}>oDE0s+!yMXCaWF!!tai@5)}{0TM>H+=sH^kHE5`X1eBgU{ns16ilzyxd zGt{|vIuS8lsx}SNo37w&;DX#Q#!R=z&$MOE40UL>H`5;Wc|M=>Y#W^&UM(N1oE`t+ z`Rd=WWowNuAw_~h;U-hiw(w)qGx^PD5YMj{GH3!~P=$QjCBow_67m-WkQb0qP>pP7 zru86r2FAEvhs_URcm$$Jlk_y_PePD=rzU7c#ybIlFFk_EYBkXucN^%_f5+$?Si_P7IpO*cjd| zNtH-DJvI8HV~)GC2nNjAaQhF`iyKImCArYXFiTsQufH)Htx&$f8$wDLkuE*~=^W9= zS(0w_rY-nsJ?hdJB*^yNHzM7lnu??>N~GmrO_n4Ug?UbS^D|Q7NGXN@9N%>IDk(=0 zo%1Bqc{CVNqPuhj8Gdi9RRMbpu~dMKe-j*Wp)*Nj%KE*A(H>{Vc;2yrUEU z@n_lc;{bipMXH{V>YrOKAC^xk3AphIpQ{l%!zX65b~9duvdy`#PPqx1T1*l3DGprm z>MHR0BWEz6CXc)-C5t zYp7xz$QL7|{%ILdO}SNY(#X$mc$f5E(G;(b^h$nVpYAu+y5={zc42KT^yk_|*vEL_ z?J)U|5jy@+i-1FbiA#PTqf+okkibKwMw#J=d?fB{OF;=zjPa-7_`LtM?T=;BcisPd z)aQKQ!BV40iFr_wm^&YBr8!jXVyxdve%HsM@sAYwfXtSB>UMzR!Fq;dK(zd)*si`1 zqR^qx)b3|uefr{g3F7&8#Cxv<_SXcyd=@w`82AuKLGA!z-PWg9@7%H$;=%$0C@Ic$ zA72Cp7TyTFUlLegf~!fu-c1N9>I^D=#SSV_|5UFovhxA(s(@m(1B)I@!C{Wfj!Qfv z6rYzzyrZT~hf7R-0DQP2HRJSo_s6||r}0n&JkSo$+z#$K_zYV5-2GGPE*SvEQ_xJh z9y!1d82ng1xak!&z6pGB3VxnNZ4C<0&Dfler7lvWxPe%k#SS4PXya z3Pvz>Res(eHq_|mwsBdg=~s5>rh3?}`o*JmVLNM~Uqi#7*LKR96R-TH$LJ-CVLRt1 z!~DDzNU?_i1c1vxig_hC){W+4eqTJGk~i=5wFi~)2JkaWm2t}VQ)3mweX7OoSEqm! zBt@#zIls0qyY53!aSYsbfM!L$$Q^YD9jzcFxO;EZ_#{3>JoTZ_JtMKFhdk4F6W)AH z6N^aZiqPb}L|}(Yy$(NYdP(~1H#Jw9>JGpGD=)vWW1xHU*^m}MkD{mxFd)H|@*jBE zDgen!OUX5r{Hj?pwI%7b4JTBGx>aBK)DHSQw7vL!^!fLGT4eK*Dv=E5)}u+1)(1j` zjk4;DrNU{9uM(*bI}fuXnPoMWuWO2ZzB?ROJ^D!1^J%2V`|oxizuVvcKD_sX$xr=q z{Y+FfSR(AAq5n@w(T0;*5imv~Pa9P%RWL{gF{`W&RH=K*ufFt#z;%njeYGJhi-1!3 z1-q&NIT;;p6CI^nZyQ7B-N8j6HsZIUCK?|qDx^9RVgF#11NT+x*iXQ>x;XaF=tl&2 zVsymCn3S`>FK5L>>;6K`sGSW18Uo<)1N3-65;^o|wiLgC%H!e5Zvi%-SiM9ZViWoQ zk@ePJQ3hQ=ba!_*D%~NeLkZFy3IZb19a1u9p7(ve z@0_*HA22`7taaaWUHjVmXA4zLQ~iPfJ?;;Nxp4SJ6eD-DK#j2Sobb~vUzLkn)%;fV zAFULSOk7--4yvrzCzY z&;45E{M4lU-@o!NhxoPg3F(x8-dSKkIYRwmQlH3gKWF0ybGKQVgl>=k478&ND}l@a ztgIh^8yi^-&yH8PqJ703HYV*ETOFCW5*ghc<6;tNW8&i!os$fK$y?&SND#*WoV-`lsP z7E#$#5idD7Cp{x1vw)_s!7-B3t~pOpi&F(9SCUA6?D&xhwi}6NC#V89%kMrRFcCLrd6s%Ol3=H-qMzSZYm)9NKdPxt;q;@M z+-;oyGVyhnkc!nYkK+yhufaG9A?L#zf!`x(?0WT%w}Q5#6aZI1W9h{>Zkb)!$7h?K zRygGn?|3!muA=b8EXUfP`HN00ci8Q>7j}D3{E+0d`P$QYVV74X_JubvPuyx@T%z^o zla_a<>x(kr;JP)jljHYa>|;GsN;l__N7x0Yel%h#Gkym@%J4i4WwR#~OxeatP-`o3S4 z&|ke@6ZaL>wV#bOtwSJ3w}7lHm&K9dX~I-%j1fDANrV!&rZf#c`71tSV+qlo(;)iL z7>-#f{4&0--Dm@$fu@29~- z_zw-8ZGIA<;`ZfscFd(GpV!B+S;@VzkK#E-Lj8zT zpt{Q^q28W`-ZMW9kD>GZ1LujG$#{=pxr>5}2}MY+N3TQ;|8ov<(y%E?aaUTwiXr1b zk;Q{&C6Q7-rZ8p67*qHdyw*l&&yI$+WJp$SoPPlJ!ptij`#48u0K9AZOJxZVk{E+` zv5G4E%c?VNjR}>B(SyQn@#;Tq>YA2_Z(X$#fi_d0e-?*jSrbUer0Hqd0dO*iwFpf* z!u53n3tX82Jo7C>2WknJ>f_{@wfxMA3==jbhgF-P)w!d)OX~PkP*mEPmyCSJFTje4m#t*YofxUT2C- zg2OfO{_7`k`g!f2z#9H?<%9dIx9qZ zG;Y%UA1qS(lnQ#IAfx8fZ+aO@r^yk`l(dN>KBySQai}QeoHIaoJsly3hhie|8c}4J zg~ckqPv^R8N)~^r3_wz^o<=EdMWQs|ocmVdR0xcpm^cit;Hx($mW}+NT%ZT!!s!u0R6O(zY zSPLtaH0a&Z3Nn7Pu2nuW^yDQ|;SUwF(HUUt*@_iVhx1r-s=Hv9)BQqM8ALt)!z^M5 z<{~jp8FMl z0fsPu??dNw)a+d0X@;FFwh=shg*dw_p;Chv2}DC+baLo!4@-!~Yr(nP>5Py3m2Wg1 zyFByP3hbngsGi6Dxh(uU_(mqMORvQEvf}n|PuZ7x{{5r}R&GqR?0hNup#B%VCVz!2 zns}^pzjA<-D9Qy{M$nDeWcNP2rtkV1ZUq8}j5mD~Y|;jT&@`pQjf2rJ^%87)f!qTL z!Qlmj**1nn8*Ok4Kj0Yk5id5zyl}LDm@NCywl@SP@>sec!{?wIH0Ac8eNmfZxyzPx zAtOkPp8z9h&jw{JRH2aRZegL_yE#+y@HN#K3TMKZwAPB18!#?uyz2$0i(DqgWND=C zNK!=4MY+v`yA8*94Vsfa@Y*`vFRvPBH~*+}w(gBLTQf%wgBOl{Snnm#pb*v-V2~bp z5Vz&k_WAmX2vdIp-*B2)j|LFxDkBu>rNIQD3|xCg8xfIc(EkwX!b z;3qZYyTF)Fns8itEmH}cP~uJU+Yy4wg&#o+ z1@z&3#15yE#(}Em^pk&WjFP~!CPR%a4+(3zF_uYhN$hk^>V0h~IvU?Wpd`NX{1nQ} zOaNo?Bgu}C{p9D22<7!pvsunWn4Um9s8T<@{xTAm6sb-b5JlO65ob#hI;_nwINX2}aa|xVYR%r;PJG7*@U;c*G-WdB2Y|TtV z@;OShCHVCJVv)ywjWfn?DH(R<3E9w(brLBo4k9m63&6N7Ei?Yn$Lp3AC*2HFs-?n5 z&q~$aP$C{C+^0E_lsZ5AB|J zFANcYno$FX_J-Z7d(Q6&je1$5efqE|Q;g763VP+g(LC;mcb>FmSa^s?y(FeM8lKN* z%1Kf`c8q&pd z56u4N4EL@%J-=UF|MxeK{qw2s7hf!GW`@xY#aMv$E$F!CZm}4a0@ns=w-~Y=Y9c^d zAz%;&;DHI{X9mRE2!r)xvLUz-KqaM6Z2RJSj2s|z<5ehLhy_*xmK?!6@_uMNCIpFM zMF0pkZmHm4KnXxCk8Y`5P$k6=*dCDry0m zN8oD!FqJm2`Afjm@fh>#xSkAv95^mk2&Mso>RSYm6nWP~k3qcgumbnrEPgyIMpapW z$TvY+8{qyDJf;m&VhcRAQjl5+iMb`I_a?cmC509Otbjal{0h}wzp7)Tk%W3T4%3`R zza7`5tyZOLXQcmAN`K7Aa9zsq$OuQDZxJvtl9w^kGcmE1G4V3R@f$-O*EPhMSWx6D zlm-(T`oEwb=W(qp$3kzmuXu3;`(UCeQm{A&BTSZ#YeEk?Onzf_6vt?i<6gGfU7)HpiEfHTGJCCYy*Z& zMxY%tqBhK|T);n9>%3uG->+Vsd?1yuDwJ?8!ay(1YMIa4ti>mh5~J4WV|l4AX6f&R zGS$qoU(02Gl|>jai;hN+?+)m#0%HTVq?5L!7p)NrzZL&%DS>R1&#jeof6J)-R&k~m z31VTieFYlC!eBvSSR{y!Yvy2vH~&W1P5&bN838yo{$LS`%L2d{#M06=r9#cWpTPdX z)2N8IVWPQ+X#60rT@TQ?%hQnr>o$k$N}A}L0^#dg~GP( zHQ|#eD=17iIELj}9*deeCIqeOLp%Ob5KhSV)Qf`}lg64qZM2+`TC?vP{@IXYY-?``a)3wfRvYML&W?orZAhclsfqB$Rn_LeDI2j0i#(8nN>VnKG)0 z*c__a-jdIXhyz_e{FI*mcs-KieE!h|R1w6*?j~5_=CI@CUh&RqCq$M#%$q$l$j+Oq zGE{UojDGjM@|K;sJ-s^;UZ6@KR zPP@bp{X&#Lst%3>uJBi}GfCe;guV6&*Y+e2_9>6Mv^Tpc^m{36Z&G>RNJBZ3C96_Z zIMcW|lLhxOtTl^pSlN=a&a z2}O#DKX(hw_tI^vvYq#ey{j^a-(-?=Wh!u%xmRUTSC`%Gm7)|J627JySXO>8C)ck?fVicg+)_l`^;ie>YzGa099m}QS_BUowGNt84#fXC2opiTo&c$N zpc+|Bt$TF^7A0gF$-E4bu?Z&`#Hz*#t)3ked{YA*;i_v7sUzoZJ+E%2=5AK2`K(*h zf_2ztz}@%www<1*{rccD!C^D`A>Sp4pmE`Yf#X$RrfkfM&e1ILm~c#32%94qw1LEC zp@6nBXa@92UQPkw;d~+FbT+j_zmXIj;4!A~CpSP@loAq`wvTPiaq`%4>haKQvv%^l zc8Zv2n*L~-%V`Gd$tYiA8l8+7{dqWMRcka;J0bgI&eloV)M;MwX#N;Y*gjb#;9WScTQ_i8-*?&s z@ouUdIlrhL(sB+gO)GyH78>ZlM;F3IYc7Yj&=mk6uJVGL82Gi}^f$@zZ6QHsyl5M? z)NUVvy%CF&)>K$Hqo>xsLk>F&Q1)rx(I4k`)y^l6&ZqmlXLr1(^uS{#z7wvKGlG)~ zYQ77clVd+c^ql8H?IcPkREi&jk%5$f4w|8`Vz%4Ce~P%CPDFHV9^;qTQBQx^E34lR zGB_wZIrw^f=%;uTrFCiUa{cIXu6uF~;=eR#xUf0_Qk(+bHe87|0-iU3mjFY$^_U)H z{QtrP(I(CZ3_B=*n_~dd2={^l*a|8&FlOOvgJTgOS*1R_2L*ogv$i1w1EDCyBhNXa zIIlfUJcxU--H00>PY6D1D4-H61_h~74l zdNjdW2FNY?{(cX+S?sgGdy3_Pj_$ZZlN$+ALh%4V_6^WOf2fD2irgp>tBXCYEpkR+BRpwAWNh&Ck4%ok9EggOWC$ zvJQ`#9FOwL$l5CN;NJKv7G8Ks8+ z$%RCX5bvxc3A+73Ai{<6|)qAznHDlNdHYwTcR*I4hFdkJgf*91HP?l;P&=(!C&JJFwmwG*YeT%Wltxp0xWGODuAFe0Wx%6MY~3`4 zX3S{aJWf^zPK~rk^|+dWbz7~gNd5a!1H&u*if4@+P#qHN1iZANpWFV;oOOuWi=+su}(4Bv`0mVD5r5DiVy`05#j6EeUTBOmt9u+0>A{IJOe zoHzm1oTZ%@zD$^O6rHgU9l61qjqtWJX}9|bt(6D&G&wDa{Y$nuSezW=nPeG{;i9Ps5+)XCq# zo##PI@1hPq1-`qvAN2C55D((f2Fqw;Xli4g5wvs+h#i7u=WXqN3ougYvM}o6_M(4n zvs{sBDapjFUc{+(Y3#AHX%xQc+`ehdo!k>T@jr+Xc4jm*k$psURnkaRF?UxT zNVNP=^-O&6QkfKt7Ir=WAhe-EC1C`PS?({(dPJ8@7Z%KJv&==7-n};Tl_&R&&Gs#o z>aOvBw?^I*o!#R>(OZ1qwRG=$Mjk3H<%&rO;K%)u=48TYxH_t28Y$(OdQYHX*u5k5 zdDwq&-QQ2=VL&}#sC;S2iHIt2nCZb2e}hUdE3i* z%F7WiD4kKZ%jh0J8PWGbs=SOV=|Z}^lB1qtU|=zJU@7LmDMI6!i`+=8n31r0_&4eNg@^n$;N%$940G?yh4ftdzZJfJ%QZ43BLBWKQy0m|j7$ak+2jrS zQdjon_auf0(JGR1guQpdt`S+)95fzUF($alua_zKR#z}A5;P(cH1^u`b0O_mDD8G~ z!M3{WpK{Y}I}9jjrK}QFV zJF1VzE{`YSK|*sfXND?MedfD~MKfnek`OcL7|>M}{lS>hLEYLxV9|6haw)R#oN@ga zSbTz7`+0`B{x>=JhLGWwW?h1f;fA94rntPx$9yUF=cZ=S#xowxr*a2JKd;`=A$pb- zj*5F_`4DhM`(qdZ&dbU@p&0E=xhMigHR&8z%YkSjcK6m@0qc=C3OR@O%S6m0iR{n1 zOeKgRfP68xKl7dGcF&9DLb1psGwgNBRa4nsbY;BJt9V|jl9{I$h5;oL?SUhZe-<&Y zomV-K8*dY-Me#||5>&ed5GI#?3I8S<6BR=A)TICi@vv-2HYzvCog+!$k3nP_;>VkV zp%C@yx0C`KLut5D?X)RhzNm8hH zS8F}4w|8qA--T>`l_ehV%9#3|jTNTK5js(OZtdFX*e!i7^nZ7<-WN?FSF{&}zcG<+ z_{BW%?&Nr7I9skbDDeL8+0N3J;^1Hu3W$d;^ z6Cx{@Rbcz(D$V$}kFh(XY=LPA?Cd)Qx95p>VX;GU_&6D?GPect+DgC|mndz8d;SbI z601y&XEBM^0VkCN@e)bH!93sZi%k?i*&GdDu^~GOzM0yS+5nq2=WPo z-LQRibCstLwfHKigdFjC4^FcYhj42CXYyH&bNHXhXCK51nMT$8{=*Zu*l4q?=IoZ* z?I$jb8s>NBIeoOYJc-!jHnJ@_Uz9!y#(vXyV?~0>TYOOFbJhOxSb3hBih*Ro<_N{c zv!X?%K;*>T!1O-E8N{@#0y>|juf69kT6!fDEcT+Pz40*T^t|sqwu&Tv z+InTPY=v;Ff26rOiO%{QkdkoOAWIC7GCd5EQ-nCqOt|uIFrh8g940R^ov*gs0fNAS z%i!oT|K7kouH+PS+=K)uZD6XR@Avg@=KhB;)pQ_m?9wlzCaXHruGX&D*RN3^t2APgQYLuh{)X z^()r5h6^|b{dq5HEGMe1=I)jU$ggXYP$E3WF4W)TiJ76le(&1J*XWS=43Hf)DiKU7 z&i-l|015f@=}hzkc`r-Yg^i-=-Ae#231ygE@VYm#2=KdIOGsCu66r2uWLek%48dXM z+`AdevZ2_iYEkduR%5cK`>t~$u-?1EWbGhGtZVa#*Soaa?95NBdm9a#uH>6v^nb!C zTAQ+0_y0aWdc_9mz?9NPCy++6+NGPE{Cy98JYc=+7`poEmhNwUUTxTa^npKM2KVRR zq@{lJABF%i+E@4uVgubGcI+vkGt>1sL)ZC8jG$6HVjl%MZdfM3s5F6a7vb?zdO!vy z5KiUO$FN}E=j-VXa1btsmLgSAq_(H=7Cj+fxl;hbc})?zBi4B02o=FBM4Z2NC{W!z zEWK&tHGh2WxUEV{-hlKULA%?DS4kfEdkM+HKXNBLWLt_hSM9{Wc~kE6mqkm`wrG`~RUkvEB#b5;y|a_WuA0G*S|Raui`t#NTxtxSj-@ zbg!e@#Xeu5CR~DCIs5uS7m5l)`P{WaYs`qD8K9duddPrIi= zC^=MU4q-Y4kf1uQWmQrgZCwfvDC6FgQzeq*h<44Cjeg7FK#=x+)z`OKf41TTVkkh; z0o=b}uN^s)^o08zi+}50aXJ6n`=t=^szGYaa@1QMhnU^o@ZS6f!A$|4IeSKN&-!i9 zyKxfP)7+)|Kiq_OpXPcGK8XF>(<$~_Xehd>d-w0alEHsvs_44y)4wCPV*j<1qQAX= z|DA*~1fVtv*Dvv=BP1?MooP?u>ks_H^u~?$C6pnX-x?J0b8<-br3UV}9Ud>3e+EWK zG2HEtqyCP)3iR@qqxwyVx*H}8x)@;ick(6ZVQwmLdn*LXGj5opU(*O{@Fr#Irw@MBp3#XVWe_(9cKej7lcCR z4x#BOI9ZL@fv4E1DH9*3u@g^mGE=bIU^q?FxHYc0<@~tAFud**oM0GEeIKbd9CliY z!jGs$nDb-VHDX;LX>K;rHn{SrMx1c%w8uv1j5f|t3aoP)wlIw|b_$~sz@tdTV?4tp znZYA!B4TcWc{O56oL#XXa94qZyl|XZAd!{@mSY&^z9nuuyptE<)#$Q1`h;BrNjF}C zMGv6k?Za0f0z4%mvaz6d7NFn*c6vjg=a!USM4j(9$k>NFzr!(NQmG1?U?n=#*)wD> zcBm_KXo}BV8k;D4XQ=y{X!6}?#)$soHTy1)$GZWY4JAC~a#V05RX-!uYoh-e0@b!8 zG7Y7)nq;$Sa&aW0@JgjPLsF;(Wl0(Ui~7)}+$3xG{ZeLNN)QGtKER z$)qvM6VoZDQCkYYqy$)>KQF|Sr)w_BmIW|KY!KD=VLg;YjLH)wjmAqKjSTM6E^pAC z1E}5Q(Y`-g^K&ZL2JSv1N3QUzeldocoh* zWEU}aTQk?tEccusPciY6zFD5gW-52Io3oFq9Z53U2XY0F-G;N85wpEcV=J_Pz)Jw8 zKKB5_dP2`g zc(R|en(18;ASl1f)7FPTZN3tmZW2<$31>@jegku}o`PmekV>2={6kAv5HQm!oo*0_ z8xw-}wNE^CF1vq2tS}ZoNQlyAk3Qg{w9$j8HJz|yPO@T8YQRIfS4iqpi&TT2%uu?_ z*qqEvi%g#fmBL1v;2x0AS9VAB%y2}k$)jU<|%1c zB{@eTy_7B;`AwRyRbh%mR!Cn~MxRdjQbs^uNxoG{)l=4`FW$UGS#zrVj`6)WlI_rf z4!{VGgP<=ILFVyfVIdF>1PIP2-&BHr1ECg#s=OwZY=UDXc}93%f)FKCM)Hw)m(O*6av$g|***?(cQJx2DgJ(4cE+U5Su8yIc@TtA@7%z5* z86!;gm)UJEi$#np7HFzP%+|fkw!EyS+N>I)QC6&?)_mU9<?tcmMe@7bhZvP-gckJ-&PsC?G%00ljS&c{kEa~?bru0 z=2FNX0G$uJ?Q!^PGjiM4S+-8s(2*nzBOp}){QeNhPz1F1P_WN#xA)kvKQnkUw&7SO z>bTO?j=mqXx%Tfi)){&TYsEs7?`va<@GB2J9%KM&yw+pB`upR>#wg)zptH@ z-)jYyT0_=GF*f~Q|M9UtGBO0nLmsWYVn0DF487kNl6#lz*C#UYZff`}y>kKih$#3> zQFvp9!E-45s^0mVDEMQt`qgCn-;4PZE{A+D48^5Ht>G2p3~p7Q18;uVhxvu~vJ}te&PASre{qO!?HH19Zyt>@yR_u9zIlGA~yS zi1&sY?NG&o13s4tXq5o)ES@YMKVwsjI0^%tmPOqEiYSeUGoy@DEsxVPih8vy(RCvU z7f%oyy=dP$vC2$n8%P-PNnGhbC9d5hZi**u`y}mkBpux(orx#EXy9sR;w>|J78cEsFX?=k&KN@z5u11iEtGBe%IEaj^~?3_cDVq zH-ptzU4<%B{bgpnQnHz^0vm#4TAm5e*Cq@Mn6M&>QhB zcz2t*l8aEV1!sML z*sL@D*wgwsh^shI8{22Gp~|@P%J{mhwcg47lqlNiEE+Bq-GSq4Ajpn4i(iER@sK9o zR<@TzC2m-y*wnZ_w&we+<@vVdBGhG1zE}85I(So8MjKb6K9v8Es2VeL9Qo=<4g_!j zFyM%e(GVyc0K$VC1_Me8*`tAQW147JJct`9yC1Epzbm^pnq-^L3mmkoo9ptQuc+&- z2>2RZw|!?5SWz{zSyk>=y=KW!`W-B}Nhi30XhF@!bOz;DH!h9@geIItCB7lFBE@1;x6p&y`#Qt?RY;>1a zd?egCKj~pNDFTY>Sm8*z=17hiNlL2hh`s-mejkys`{HCER!b^Q_Xo96KG3!gEgdK9 zp9qClw!4&l^6>vOW8WFx-8uE+(_DA*;(f>MNN=J^(l7tSqWspQe7Oo)r`xTFB6&Pd9TyL9*-!@a?HpA4L`Qw|Z z$Hu-Dj7>?Sn)60qtl>1Kn1V6_h!aANco87U5W1WStdBjTZofwVRbwUvjLkeW&zp`< z(9R6dj(_PHr&ybzF`HQH8Q~0+JsoR(k4buFp^fQI@9asS{JP!YpuL2x=e#)Ew@D{lO!fJDOD zdH;iXy|q4g!lGH=lHJ;OH?zf-e~Z?IKb+_m6&(e*16RLo&DhkAjyh5@A%V02h$n#3 zXcPR~3_SUs{aX&?*^|+awWGS4V;zS}Hi^^rPd0otm(1}u-0Bvf^qYa2nu(*kCL4juv-Moah(1KCgieY^ZOUyC>PP`H5gxam^2X+r?tFPPa0Mh!-TPYL0Lsk21l=DO zrwYvQ|IAG|M6;~y0)`j#tewZr|_NCKza=hc;_*OTeWF6Ppd7|_OAsyNoSZS|gQo{cf(`xNh=v35m%$KULVb)WHvGVa^-Y49=p{@Ta9B%V4z@>5@4O_Z1A zcck1xt80d`O~JrdDPKf-JTxjnaaRZ$p!2+<_|GgvN;9K;M4%o?lw4hMJu_L;G-+H@ z@#zmvO>OJG{Q$0&jp0y?Hw+_66yA{-zijZkM?p6gpnX5(X zhCu06MWMi}CA%+ym%sgkgU~`#l+4|)8Z>3P_s&K3=5IrB@HI-z{=RXsmlsWmnPF)G zK-f=909;52+?F4_ft~F0Rk7$CW4UK%COPmfpW*S>&S(5o9rRyE-}UPjSu=6n@t zKTEV5XQulbqbjjKlN69(&G^S?Sn66QC78tqj@v{OyO$Czi}4==KxjE41jr1HX7mXG zvn!m*>Ao)`YpPV{^&?8KV%cIQ!X8nkcTb5EX5pbjagLcR&L+q5u<%*GQ8&Iz(-KFP z3M9vl+c2erx&T-hOqPUPo*~rjHweO{5(LOzNEeFi!#>W^xm820sHC2kAs8K2fkAQ$1TMurAh>aG5RK7 zzZAo1R|ztz9S#yMrA*vaVTcR2Qc1|2jQEJQj7~7ShJfQnK*YjLgq#ti1-~@ZB&KpF zz1rvApAJ?jN#N+jgWAfdy*QMG?&hM2MXFSzUV;I<bX?wSI!TGA^3coIF%(quRYP zU3A@&U1(%@%Vp4YZ83~KgAsn++c<-9|R(Id;dvBOXdoX^jt@@9Zt+2(`BGx1K z^myanaanEEPRm>oA}R=2y*2z1WrV_)r)C z|B}*~f-@v2xZ4W2wTy%s@Q3U1Y~d?X4eBNdWWG$;}{guDEjIH(-a;}Q)s#HJc^UFI z`%L&78V(n|WU}&lf|~UF9)62W^&&MjJO|d+E6a^4qAhO{9qd&7S5gC7+ZxPvuW3v_S<3ShI*v3J1{+dwA-|h?Qli5j{pI3{2d+$?D zwvTDn|B>H(0SB#wf_paBGz;51od^+*aNH{dlU&gm=#`N2<`LUeA61f3Qh2}u!zZy` z%}JLG64Py5JBfax_@kUL+M3^*2sXx)5PJRr!j~mO;=&)p-byPH%J|4D;=w>nCQ5*_ z!$E1Y6adsla9`$0_*_#-c*tZJoj~R8!dr_eVaGsB^J4bpLPl@zl)}8&r56Hu~OY;3fCj0;T zNt$BTLjfRR*I~*S7_RX=67x9-VES6XFe>0Ic^!sHvs(%UI0ETHLtbAEPZRKB>k=|m zmT}xXEj`Od-P>xQc)qy=UG5M)9#Ww0mx3R!rx;M@hQSY4&oziz{CofU_o)I03SXUh`0bPKXhjryG}vgGU>!+#>xx6g4_?0{oZ4W>YbG#7 zP2&fY;Ekq-tDoXdSzy?lM(9-H%_$OUA|u-o+}a$G=Fq6u;!&33kXMvZZ;Ybe`b3%S zMwvmQohS)yo<=*yM7^tw^5cjOxQPnd#la^L5U&iE9(h7?8m^T>PTR=f2bTg^K-8Bb zj+-NOpGNLjL>g5_`q)Q#@5Xv{M3?SHTR~%dDWg9=jjIxmE*6gq9Eq#gjT_~U0oa6p zii!7Q`mZ1HNeYZ41#$$(;!D9gF2O=8k#68n2qWM*X(-O7;Q#6pg3D@<{xTWzgD4^4Zg*Kg+3i;>qi~ zsmw2vk7JS~x?+N~e9g*cB+r5KhGZ7!#2I7X+c)DM-}< zKPLq&0uv;OAbr(Ha4;D1c^V{%EhTY^c?{1SvA`Xp1gd`HFhREG66H8;&50fWaFK8BU-aRmR0O@K75f9s)yq zNig3 zGmIfftQ9FXt$W6X1(f4gfz@y9XCZ{TjfHEmVN+B^TQ7@##uoj0R-}q7Jj5~P(Yssi%9f_n*^ znb`a+?ZRVM^rs4Wbc^p-ikZI`-P{(laFuZSl|5D!KXEAgyH|EjRl?x_#W2p7s4h1Z z0Fs>Ijv_!sB?R0^z&t-5(FTCU0z)AMZv@o{3J4)|0>~ePaXxLL`F~xD@b~|J*J6Us zd+WFlfvF$gN>Vy?VYtM7bQt~Uu2XRXsPK`j@N=ke%d2o6t$4>(p^vBt-LHtjsf^^R zta5XP&6UehzgIMVueuK)Kw$8?0;*jZjubw)Wx)*dKA3#}@QV7Qwed&0yUGN$%0R!$ z*!{|Qu9`;4N`Hr%;JfN5znVDen#8;seTz3zYPCaLWc}jr|0`MKCj{%5@FKw9rU@_k zahP0TI=ZpAK$1r|(f`^OEr)9VV_V#)^8%>R%@F>9Lq$^S57g?8aT<-$^-?n+iw|UpJx!t$<;C_-l_!NxW5z5^WuHF&( zwj(;eBetd^eyk($pd%T#GnKnDUA;5&ZD)3T=SCkO&w_xJNLSbuABBPumWM=8e{6WRQt0cFFC}SHC;5a++IdeR-Phe9)vb~$ng64cm`L~420jBL zRLh;N;zxJ&kM2hG?(Te&8L95BgYE^~p7yuBtKGd{xVz;Udveu32VGOYb|cqz#s3%r z3Wa@uy{B>Sqai<|nJT3nn4z7~p&PRN@}L9x=d7|;!IWjJa$(HwR)f;BZ1yNYwrAsPcJ70kJ!~$5 z>?thyrVIHh3QE{#^d{2~bTj}CC^o3AlcXJ#J{-WEh0FJ_9;*&I9uDf$4nLJ1@}y` z=Nk^=U+U*u{KW@K=P&5t#{rLG=#9P)7@b#$AtV`DR~U(P$CqiuywrxOHex<|gCPy8 zP9NvYl;+Ad<;zPLt*IRaSdWebjAONomp^1lv7J^blt;nq>$hck4b4g@q+^K;|JW0WpTj7)EVvceeD$WznR;FslN|X zOlGt1ttU8pUr-K~v|3A%{=yI60382FAc|;B-Qykdkp9*p4gM`X#H_&Ssla`yAn&BW z&ocisa9(9XLBnjG6+y^1@0GfeaYe*XCc=Wh@{aVdWQ|$}d}$ z&4dFjg;ic>sP|v0Wyq`~wyG!0t7j&z3<#?iq9#`IpQsDn@Tbo!%Kct4Im`iC;Doz@ zXe^dvoYdkA)siOEQt7@UFV#CpHA(_k%GQ1$dw=BqTj{3@@k?C&s-%1Dsk`8*$sB@l z3B#7*hwK8Li6Hbyu>oR{TKj`qtXEoR`uY@P`dc#kdRO|}6Z+b(f9h`Q|L)b-ANqOl z58Q6}U5ZR|?)NGeCV)8wtE+@i?ssFlk=}2XpFb1V^|if@*7S82Ht=NE&+C2?)35KD zZJs9n{Oz>_=MM(U&}N^c}ucrOYZZQ zanRQ6_LlhLwkSH0=xp|3Ve^fw;_uhy67A+kSLSCg09dEyjQmMrMlF9e5uyoUo? zheOVX!%2tV>JP`94@pk3?luVj0>F~fIVM0yT%v>ekB%myPODmuv)4!S zpOrd{*B>7o)8ViHY%B=p9vc@?k8a1_?vLIlSlK7ohVC~-?vGlo#;~q4R~+KSld(+F zrJ-Zosnfa{$+JgLFf$NC8&IKgKtbgDOy2iHG-kG#7fjpBaLM=Osc+McUlqikgU_EW z`CV-^rUHPVei|H?La2*8cYw$n7rtdF@iP6iiBEVb&qs!Zfc!lL67c~&5cr*Kgwd{N zFmYCD`@rRjz}20=o#D`XyRave;rusYrl>39SAh!ln6$1KzmSlp{DgifKobYzswWmV#;4*|e8d3)sHUOb)m_JM6Bd3p+O01mn* zn(R&i(o+3XoWu1&X1|EV>pT_G)K2_ziZ+nkA2D2Z^WE^F=cIrKoqAFvIQN-AuKmkg zPXO#Li0a$f&{`%fBxU71oUcEgdDyfsHd>oeF&f6I+xp)O6We8u0E=oGrMxq36@PZ3 zYos|3`kZ94D~U^_nR-5X{c|uXNJOcLk*S788o=}V`~03>rOpSf55^1oFKS=5Id6Yo zI52FqL=7denk*i^Y<;s(|N2M!AyD$K_IQpPf<%lGj?mafeu&nmKDhj?z2T&fOZL;W ztwkv!N=ilwM>S62InMvn-kAqAmF#=G&q+cS1P%!xI{^X&1Vj{!vKR=+A}An(C;d8gQ(?rg5eA=Bwq8)4 zBj=!vHt$nlTYXuP!e;M_kEiv&ddq3cRMy3~@+% zc6V4hXq0g8oXq%@F^FWMy)yjf9fO?W#ofaYo>KPauBp!k`gaeOzwg}jP<^vzahT`S zaQB+UI{J}az9!cehx;1g^dv?apKDL%a``@Rkgi!qxJG?g?TP$*N4P!SKD>nB=#3Sb zQ5#aPO+~T4y9q?r=S~kixkzsq5Y#XjSJ#~_!EuDsCn655m!H_~{dHvazBUT^J zZ7a?!64Z#3gP*d0a{I*2=>BF;?C$58pJI8h{;?Z8`SYIm{F$jw@hHZC#}R0{Pjf_+ zxhP(O*kt!~g4FsDFR^IB_36ZYiy!kge(UjZdSeO8ASzoVEEx&d1N2WkQ4Ol+(rTUsBE-jLP0pdvy1gE%NF^*;~(Fxc+79 z#qSHAmm-%Q5O!tE>IAsuwaWZ>)U*%i>oHGITRhS6$g61~SzEyn@a zS`CCwL`3udkm0tlB~_% zxMM-H?p%nR316QI4B0fXF-^MC$+Q$Du_F#v(?(hyRHCSDrwuq%ADq(09`PeZ3Al*ch;0c1)FRO;jAIavX29Hp_MF)-4R96wlZv{9K!2f)5c{qk<6KR z@M`#uCPU?PF?Y!s>vzGJ$E8w(463sQuW24#$>&64NS5lNc8Z2HL2Y4#aOOx@=TrAW zT?S7c&Q-eQmx^dqU%Z(=8BgxPYGyJGxzWB0dm-FqK~3;G#6IW(i_eio){q;LofEPv zu3f)YJ2}nL94ON^{7?>?sCOtw4~JIUevA)GhUs-3*yuB4U5s+mT4V0FE0dTDDPzFv zqDpA3&>j;Z9f0WN4HDPMPP_=6oOZ#RU>e=(k;@yBBB5)~4tv|%INcQ)b_0jP+O)#xZ zR&TNp7c1QB;pw8Y8 z)fQ(1F{0lgj9E{?DSa)hw$;$L`Ry&_s@R7Sj9YQ}QU-<+-Rk+^r$P#I(2yEcxARt) zPTJ@zon;*5>YOhH_!})mKl!75EYG2c<3Sb!Tw$jE%kQK zl?ItN4zdCRD8qHq_&x#N4MEM`*GVYlj7Bo`7tC}&ypqkrt4f_y0h3S6Ks$qFlDH%A z{C*~Y@6iji_sNRW=57l~WbU%g&D330&pizr5!j;#9Ziyz0v0IYh|&A@NP3J^Hc0Xy6{tAl|x?uKdy?eXa0Ad1@rMqE55&)48_Igjowf!7MMjF z2vy#hlc3WBR>P|NQj0L^vs=xg{860WB%CJ~>sMoP1EHG$j8Rq)ax^SC7fMg#5(A6o zw&r|W;{q&>LMz9_p_5m)DWDb)fw{|V;+ZmChF9lL*qw&Vyh@Q?q|o&Oh6N;{lRuc2 zG->{Ju14l1#Ms-2n6??srYY!SO>Y;+!&s?kKL+E%746?Bh|0D(Te1*A0n{K~>*D1} z|Bj)8A0YgM!CTo1fiZ6hPzaxOw`g(O0^p>?`!V=G3P}_-24U#q?v=k=H!V~anzb*;Y{ht1N?14Dxj|zm(2{ZQ=ssA4%iC5qqhqj4WZ2qVn3x| ziBzC5Cf>{y(kq2a*JGW>(4{g7qmmyIC^QEL{m@oVgcO`h`m{`qaD~@%BodS7Njj*;yJr0Q(Q|eUVi`~3-iy*3aPRKn!?}UR9^@w0)FZlGh;83dGE>kc99kJBGqNWj55eUFK4}CjfJ!M>EDS>|x>3 zjy99bE;W*Iaf&UOgUgeL-{hb%DoOU32$%fGvQ4NphS@od->q7-x;xaZfJ~AK_wT}v zeCK*j%~Pb>vdR82!2nj;Y+8iE%x_neqswhjM2;E(K0Mt`8a)RS(A0E3QVBE^Ygt~< zX90=@5yOdKSjjhLfYnYHvAs~muk}8-9^=5sO&f~hgCd6+d_>uExH&S^>5ymtTHTU` z(bsAaaMQ=Bhn4v=`&a&MDu#WYk4O)AhN8w?e$&KS?>xTWNVgVKdv4SBw&C@D+EhSj zc#LIuz+sW$J>6P7D8%13;b(qQ4M@2JkV@#2Wo_xz$6E^kv0Xr>^L($MJ>-p&3i1o z)LVSN_ds&dw(#o9j9y+gp2>T%_xMwrL4c)WM`rA@mH`@osmk-TX~<6AdAY$(zXXzn z3Kprr{%;m&DqU-QBLHnD*VX>ASz5-T97vzkq$OqVHwb^PIxd z)`_R>ezl~QS=kizQ(aHf1hG%<$I`3%pS+8;8j5|Y^}^OTuFvVk^Gp5x{&CNKj(w^B zVmLEy=vx0TxEHT#Uc7XR8_9cNlh~hMTJ1tQv&@+0Wfkx38oz@4a?C&8$07cc*UL|# z@gL1zPH&H&{vrPJrT8yx@iX`1XZz!Sk1a+4u>6e0kpKXW0KA8Z00DsD3n&Crf+Ym@ z5-4AUTvhxEkq(8;9UVKhqEO%R3yaAgYLVhsM6}W;ECUW$vrf|rCz3jf9sIBAy-82* zES(R-f4-KD`3ge-Qoj}pAoaP#Kl=O2hQ9^U@b^IGz#@Vv9YLFf1d0Q@U2&>Kf>;M1 z3#?1@BLH$rpR|s2%DkUabHfB!iWYPJd!IstNH`nn9#-&XT%$wi z-N8JOo3)GZ(0Vm>hv_mD6Bvx01EqZ_zt(SAzb5U*g1cCoIznDmah@;6Kt!*QGa*a- z!aCHdgG>xjPrAIIM1KGT@IAYehQ2_H5HW!ZU8621=Ch~-l1&m)BB5Dm4=PQxfB9Ir zF)%X43d^De619k&-3TV*48gM4c{zacPX;ziF8p?KG#+?e>Sqa8A}1{`nKuqQ*{VgV>O7{&V=B{4<<;D+8Zd;m8WsM zgfa7E{ujLhZCLV4Ktf4I|njfZ_#_(lS0N>Sr(c?s?v*C+Hg%5MAz0uiN)z(HXk$PcAkQa*I7e&=fW#vsOt^%I z#9jfwg8&_S#2iO-hD$GM7-5u6)c2KXB<~fYD8~0^*AYD&xtFH8Q8?A6#1fyBDt+}X z7QeoQyFK24ASJ*VdvFCq3sp=bF#(z<1Fjz z9FCSM?M1X_4u$sRW8njYHXj+^2k#f~3a5oyB{V07b^1cQx3@3eS)x9}F`3a5v~=NZ z?mvUp5G-L0JyRKIz#4J@1V!de`KF>)tThg@{>-ZhuWbMnE4(V6r~1jo-!Z+CDiL56 z{p*^=s&ixM<=6ofI#y;WRGaNy{@r-7FEh?}pg5lS`Snj>%@`b(nUD;6R<%!Hsn27h znR=Ag#9cB(sLxd2N4RLYUcbE(YpW!sl>_5GE=;((>rjm>U<+I)TybI$-thxpo7cqB zEm0Nv^!bzDHg9Nl370OFqvUitFm0@bNPhPap;CyS8%vaKsE|PumZiRr4ka{l|G>1f&Jh3Tx=$!ond8AH;r}D;A2B7tqiK@ z&8z2yp%I5BV7)OVH8FwYN$2bDD^Z%HjN=gI0P#F$#EO7oSLTu_Oc#JjVG4f>g5Z79 zHV&iYvVZ%1z2kBiYbz^Qb+uWWEN!;-ep*QDb3I(E^dp9B35G)yRdo%U+;13ZhEPx8 zJCbb7U0j{H@2`e-EWsIha_2OOd6ou=17=#$V@%@|>N3+_7*EbtdrfxOpULncq}TyS zC9=i2fKX{!g=ep&4)s2Z$aHbrH{lE#J5D<3$hrVRg?yjVp-36gqZe9e5C}q<8x_Ug z8gL^K^}ql5P_cVKjjPt1fJucE&simRu3(1mxpxqw8curXbAA8?e zK7y^0FK}VQr4aN_NXB1k$zL{HR*MYj?;>Li76BzwA!Gh$k>N1^M|%DzLQ-3egTTLq zq}DWhHV=YD1z+_(?wVG`x902G2k%n@5ZVq<%!09R#uEcdscWK^7^T|;l{>C#%*YDg z6k53u^WPHSzl`u-Z}>l$=D+-iKV$uO{{QL4+Fxe#UvKz#{vH4Ir2Rj8;(vRL|LpJo z%ryU(Blc&k|DJ#T^ZfH?Zrs3EqAA0L|09+_F=i8Fr&@$~Gw&q2E@;K-J4KZ97Hzy+ aWU*|=F+<(qcClqp;*#|dN&khCd;A9g(8)yr literal 0 HcmV?d00001