[Fault-injection-developer] [PATCH]kmmio against kprobe-2.5.50-BK2
Status: Alpha
Brought to you by:
rustyl
From: Louis Z. <lou...@li...> - 2003-01-17 05:07:39
|
Dear Rusty, Following is KMMIO patch. It needs kprobes firstly. -- Yours truly, Louis Zhuang --------------- Fault Injection Test Harness Project BK tree: http://fault-injection.bkbits.net/linux-2.5 Home Page: http://sf.net/projects/fault-injection You can import this changeset into BK by piping this whole message to: '| bk receive [path to repository]' or apply the patch as usual. =================================================================== ChangeSet@1.951, 2003-01-17 11:30:25+08:00, lo...@ha... Add KMMIO KMMIO is a Kprobes add-ons for placing a probe on MMIO access, using register_kmmio(), and providing a callback function. This is useful for monitoring driver access specific MMIO address. ChangeSet@1.950, 2003-01-17 11:05:19+08:00, lo...@ha... Merge arch/i386/Kconfig | 9 ++ arch/i386/kernel/Makefile | 1 arch/i386/kernel/kmmio.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++ arch/i386/kernel/traps.c | 4 + arch/i386/mm/fault.c | 4 + include/asm-i386/kmmio.h | 33 +++++++++ include/linux/kmmio.h | 67 +++++++++++++++++++ kernel/Makefile | 1 kernel/kmmio.c | 149 +++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 427 insertions(+) diff -Nru a/arch/i386/Kconfig b/arch/i386/Kconfig --- a/arch/i386/Kconfig Fri Jan 17 13:03:11 2003 +++ b/arch/i386/Kconfig Fri Jan 17 13:03:11 2003 @@ -1552,6 +1552,15 @@ for kernel debugging, non-intrusive instrumentation and testing. If in doubt, say "N". +config KMMIO + bool "KMMIO (EXPERIMENTAL)" + depends on EXPERIMENTAL + depends on KPROBES + help + KMMIO is a Kprobes add-ons for placing a probe on MMIO access, using + register_kmmio(), and providing a callback function. This is useful + for monitoring driver access specific MMIO address. + config DEBUG_STACKOVERFLOW bool "Check for stack overflows" depends on DEBUG_KERNEL diff -Nru a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile --- a/arch/i386/kernel/Makefile Fri Jan 17 13:03:11 2003 +++ b/arch/i386/kernel/Makefile Fri Jan 17 13:03:11 2003 @@ -32,6 +32,7 @@ obj-$(CONFIG_MODULES) += module.o obj-y += sysenter.o obj-$(CONFIG_KPROBES) += kprobes.o +obj-$(CONFIG_KMMIO) += kmmio.o EXTRA_AFLAGS := -traditional diff -Nru a/arch/i386/kernel/kmmio.c b/arch/i386/kernel/kmmio.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/kernel/kmmio.c Fri Jan 17 13:03:11 2003 @@ -0,0 +1,159 @@ +/* + * KMMIO + * Benfit many code from kprobes + * (C) 2002 Louis Zhuang <lou...@in...>. + */ +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/mm.h> +#include <linux/slab.h> + +#include <linux/kmmio.h> +#include <linux/ptrace.h> +#include <linux/preempt.h> +#include <asm/io.h> +#include <asm/highmem.h> + +static int cpu=-1; +static struct kmmio_fault_page *cur_page = NULL; +static struct kmmio_probe *cur_probe = NULL; +static unsigned long kmmio_saved_eflags; +/* + * Interrupts are disabled on entry as trap3 is an interrupt gate + * and they remain disabled thorough out this function. + */ +int kmmio_handler(struct pt_regs *regs, unsigned long addr) +{ + /* We're in an interrupt, but this is clear and BUG()-safe. */ + preempt_disable(); + + lock_kmmio(); + + cur_page = get_kmmio_fault_page((void *)addr); + if (!cur_page) { + /* this page fault is not caused by kmmio */ + /* XXX some pending fault on other cpu may cause problem! */ + unlock_kmmio(); + goto no_kmmio; + } + cpu = smp_processor_id(); + + cur_probe = get_kmmio_probe((void *)addr); + kmmio_saved_eflags = (regs->eflags & (TF_MASK|IF_MASK)); + + if (cur_probe && cur_probe->pre_handler) { + cur_probe->pre_handler(cur_probe, regs, addr); + } + + regs->eflags |= TF_MASK; + regs->eflags &= ~IF_MASK; + + /* We hold lock, now we set present bit in PTE and single step. */ + disarm_kmmio_fault_page(cur_page->page); + + + return 1; + +no_kmmio: + preempt_enable_no_resched(); + return 0; +} + +/* + * Interrupts are disabled on entry as trap1 is an interrupt gate + * and they remain disabled thorough out this function. + * And we hold kmmio lock. + */ +int post_kmmio_handler(unsigned long condition, struct pt_regs *regs) +{ + if (!is_kmmio_active()) + return 0; + if (smp_processor_id() != cpu) + return 0; + + if (cur_probe && cur_probe->post_handler) { + cur_probe->post_handler(cur_probe, condition, regs); + } + + arm_kmmio_fault_page(cur_page->page); + __flush_tlb_one(cur_page->page); + + regs->eflags &= ~TF_MASK; + regs->eflags |= kmmio_saved_eflags; + + cpu = -1; + + unlock_kmmio(); + preempt_enable_no_resched(); + + /* + * if somebody else is singlestepping across a probe point, eflags + * will have TF set, in which case, continue the remaining processing + * of do_debug, as if this is not a probe hit. + */ + if (regs->eflags & TF_MASK) + return 0; + + return 1; +} + +static inline pte_t *get_pte(unsigned long address) +{ + pgd_t *pgd = pgd_offset_k(address); + pmd_t *pmd = pmd_offset(pgd, address); + if (pmd_large(*pmd)) + return (pte_t *)pmd; + return pte_offset_kernel(pmd, address); +}; + +/** + * Set/Clear pte bits + */ +static inline void clr_pte_bits(unsigned long addr, + unsigned long bitmask) +{ + pte_t *pte; + pte = get_pte(addr); + set_pte( pte, __pte( pte_val(*pte) & ~bitmask) ); +}; + +static inline void set_pte_bits(unsigned long addr, + unsigned long bitmask) +{ + pte_t *pte; + pte = get_pte(addr); + set_pte( pte, __pte( pte_val(*pte) | bitmask) ); +}; + +void arm_kmmio_fault_page(kmmio_addr_t page) +{ + (unsigned long)page &= PAGE_MASK; + clr_pte_bits((unsigned long)page, _PAGE_PRESENT); +} + +void disarm_kmmio_fault_page(kmmio_addr_t page) +{ + (unsigned long)page &= PAGE_MASK; + set_pte_bits((unsigned long)page, _PAGE_PRESENT); +} + +/* the function is only used to make virt map to bus */ +void *kmmio_invert_map(void *virt_addr, unsigned long bus_addr) +{ + int offset; + pte_t *pte; + + if((unsigned long)virt_addr & ~PAGE_MASK) + BUG(); + + offset = bus_addr & ~PAGE_MASK; + bus_addr &= PAGE_MASK; + pte = get_pte((unsigned long)virt_addr); + + set_pte( pte, __pte( (pte_val(*pte) & ~PAGE_MASK) | bus_addr) ); + return virt_addr+offset; +} + +EXPORT_SYMBOL_GPL(kmmio_invert_map); diff -Nru a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c --- a/arch/i386/kernel/traps.c Fri Jan 17 13:03:11 2003 +++ b/arch/i386/kernel/traps.c Fri Jan 17 13:03:11 2003 @@ -25,6 +25,7 @@ #include <linux/highmem.h> #include <linux/kallsyms.h> #include <linux/kprobes.h> +#include <linux/kmmio.h> #ifdef CONFIG_EISA #include <linux/ioport.h> @@ -530,6 +531,9 @@ if (post_kprobe_handler(regs)) return 1; + + if (post_kmmio_handler(condition, regs)) + return 1; /* Interrupts not disabled for normal trap handling. */ restore_interrupts(regs); diff -Nru a/arch/i386/mm/fault.c b/arch/i386/mm/fault.c --- a/arch/i386/mm/fault.c Fri Jan 17 13:03:11 2003 +++ b/arch/i386/mm/fault.c Fri Jan 17 13:03:11 2003 @@ -21,6 +21,7 @@ #include <linux/vt_kern.h> /* For unblank_screen() */ #include <linux/module.h> #include <linux/kprobes.h> +#include <linux/kmmio.h> #include <asm/system.h> #include <asm/uaccess.h> @@ -165,6 +166,9 @@ if (kprobe_running() && kprobe_fault_handler(regs, 14)) return; + if (is_kmmio_active() && kmmio_handler(regs, address)) + return; + /* It's safe to allow irq's after cr2 has been saved */ if (regs->eflags & X86_EFLAGS_IF) local_irq_enable(); diff -Nru a/include/asm-i386/kmmio.h b/include/asm-i386/kmmio.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-i386/kmmio.h Fri Jan 17 13:03:11 2003 @@ -0,0 +1,33 @@ +/* + * Benfit many code from kprobes + * (C) 2002 Louis Zhuang <lou...@in...>. + */ + +#ifndef _ASM_KMMIO_H +#define _ASM_KMMIO_H +#include <linux/types.h> +#include <linux/ptrace.h> + +struct pt_regs; + +typedef void* kmmio_addr_t; + +#ifdef CONFIG_KMMIO +extern void arm_kmmio_fault_page(kmmio_addr_t page); +extern void disarm_kmmio_fault_page(kmmio_addr_t page); +extern int post_kmmio_handler(unsigned long condition, + struct pt_regs *regs); +extern int kmmio_handler(struct pt_regs *regs, unsigned long addr); +extern void *kmmio_invert_map(void *virt_addr, unsigned long bus_addr); +#else /* !CONFIG_KMMIO */ +static inline int post_kmmio_handler(unsigned long condition, + struct pt_regs *regs) +{ + return 0; +} +static inline int kmmio_handler(struct pt_regs *regs, unsigned long addr) +{ + return 0; +} +#endif +#endif /* _ASM_KMMIO_H */ diff -Nru a/include/linux/kmmio.h b/include/linux/kmmio.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/linux/kmmio.h Fri Jan 17 13:03:11 2003 @@ -0,0 +1,67 @@ +#ifndef _LINUX_KMMIO_H +#define _LINUX_KMMIO_H +#include <linux/config.h> +#include <linux/list.h> +#include <linux/notifier.h> +#include <linux/smp.h> +#include <asm/kmmio.h> + +struct kmmio_probe; +struct kmmio_fault_page; +struct pt_regs; + +typedef void (*kmmio_pre_handler_t)(struct kmmio_probe *, + struct pt_regs *, + unsigned long addr); +typedef void (*kmmio_post_handler_t)(struct kmmio_probe *, + unsigned long condition, + struct pt_regs *); +struct kmmio_probe { + struct list_head list; + + /* location of the probe point */ + kmmio_addr_t addr; + + /* Called before addr is executed. */ + kmmio_pre_handler_t pre_handler; + + /* Called after addr is executed, unless... */ + kmmio_post_handler_t post_handler; +}; + +struct kmmio_fault_page { + struct list_head list; + + /* location of the fault page */ + kmmio_addr_t page; + + int count; +}; + +#ifdef CONFIG_KMMIO +/* Locks kmmio: irq must be disabled */ +void lock_kmmio(void); +void unlock_kmmio(void); + +/* kmmio is active by some kmmio_probes? */ +static inline int is_kmmio_active(void) +{ + extern unsigned int kmmio_count; + return kmmio_count; +} + +/* Get the kmmio at this addr (if any). Must have called lock_kmmio */ +struct kmmio_probe *get_kmmio_probe(kmmio_addr_t addr); +struct kmmio_fault_page *get_kmmio_fault_page(kmmio_addr_t page); +int add_kmmio_fault_page(kmmio_addr_t page); +void release_kmmio_fault_page(kmmio_addr_t page); + +int register_kmmio_probe(struct kmmio_probe *p); +void unregister_kmmio_probe(struct kmmio_probe *p); +#else +static inline int is_kmmio_active(void) +{ + return 0; +} +#endif +#endif /* _LINUX_KMMIO_H */ diff -Nru a/kernel/Makefile b/kernel/Makefile --- a/kernel/Makefile Fri Jan 17 13:03:11 2003 +++ b/kernel/Makefile Fri Jan 17 13:03:11 2003 @@ -23,6 +23,7 @@ obj-$(CONFIG_SOFTWARE_SUSPEND) += suspend.o obj-$(CONFIG_COMPAT) += compat.o obj-$(CONFIG_KPROBES) += kprobes.o +obj-$(CONFIG_KMMIO) += kmmio.o ifneq ($(CONFIG_IA64),y) # According to Alan Modra <al...@li...>, the -fno-omit-frame-pointer is diff -Nru a/kernel/kmmio.c b/kernel/kmmio.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/kernel/kmmio.c Fri Jan 17 13:03:11 2003 @@ -0,0 +1,149 @@ +/* Support for MMIO probes. + * Benfit many code from kprobes + * (C) 2002 Louis Zhuang <lou...@in...>. +*/ + +#include <linux/kmmio.h> +#include <linux/spinlock.h> +#include <linux/hash.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <asm/cacheflush.h> +#include <asm/errno.h> +#include <asm/highmem.h> + +#define KMMIO_HASH_BITS 6 +#define KMMIO_TABLE_SIZE (1 << KMMIO_HASH_BITS) + +static struct list_head kmmio_table[KMMIO_TABLE_SIZE]; + +#define KMMIO_PAGE_HASH_BITS 4 +#define KMMIO_PAGE_TABLE_SIZE (1 << KMMIO_PAGE_HASH_BITS) +static struct list_head kmmio_page_table[KMMIO_PAGE_TABLE_SIZE]; + +unsigned int kmmio_count = 0; +static spinlock_t kmmio_lock = SPIN_LOCK_UNLOCKED; + +/* Locks kmmio: irqs must be disabled */ +void lock_kmmio(void) +{ + spin_lock(&kmmio_lock); +} + +void unlock_kmmio(void) +{ + spin_unlock(&kmmio_lock); +} + +/* You have to be holding the kmmio_lock */ +struct kmmio_probe *get_kmmio_probe(void *addr) +{ + struct list_head *head, *tmp; + + head = &kmmio_table[hash_ptr(addr, KMMIO_HASH_BITS)]; + list_for_each(tmp, head) { + struct kmmio_probe *p = list_entry(tmp, struct kmmio_probe, list); + if (p->addr == addr) + return p; + } + return NULL; +} + +int register_kmmio_probe(struct kmmio_probe *p) +{ + int ret = 0; + + spin_lock_irq(&kmmio_lock); + kmmio_count++; + if (get_kmmio_probe(p->addr)) { + ret = -EEXIST; + goto out; + } + list_add(&p->list, &kmmio_table[hash_ptr(p->addr, KMMIO_HASH_BITS)]); + + if (add_kmmio_fault_page(p->addr)) { + printk(KERN_ERR "Unable to set page fault\n"); + } + + out: + spin_unlock_irq(&kmmio_lock); + flush_tlb_all(); + return ret; +} + +void unregister_kmmio_probe(struct kmmio_probe *p) +{ + spin_lock_irq(&kmmio_lock); + release_kmmio_fault_page(p->addr); + list_del(&p->list); + kmmio_count--; + spin_unlock_irq(&kmmio_lock); +} + +struct kmmio_fault_page *get_kmmio_fault_page(kmmio_addr_t page) +{ + struct list_head *head, *tmp; + + (unsigned long)page &= PAGE_MASK; + head = &kmmio_page_table[hash_ptr(page, KMMIO_PAGE_HASH_BITS)]; + list_for_each(tmp, head) { + struct kmmio_fault_page *p + = list_entry(tmp, struct kmmio_fault_page, list); + if (p->page == page) + return p; + } + return NULL; +} + +int add_kmmio_fault_page(kmmio_addr_t page) +{ + struct kmmio_fault_page *f; + + (unsigned long)page &= PAGE_MASK; + f = get_kmmio_fault_page(page); + if (f) { + f->count++; + return 0; + } + f = (struct kmmio_fault_page *)kmalloc(sizeof(*f), GFP_ATOMIC); + if (!f) return -1; + f->count = 1; + f->page = page; + list_add(&f->list, + &kmmio_page_table[hash_ptr(f->page, KMMIO_PAGE_HASH_BITS)]); + + arm_kmmio_fault_page(f->page); + return 0; +} + +void release_kmmio_fault_page(kmmio_addr_t page) +{ + struct kmmio_fault_page *f; + + (unsigned long)page &= PAGE_MASK; + f = get_kmmio_fault_page(page); + if (!f) return; + f->count--; + if(!f->count) { + disarm_kmmio_fault_page(f->page); + list_del(&f->list); + } +} + +static int __init init_kmmio(void) +{ + int i; + + /* FIXME allocate the probe table, currently defined statically */ + /* initialize all list heads */ + for (i = 0; i < KMMIO_TABLE_SIZE; i++) + INIT_LIST_HEAD(&kmmio_table[i]); + for (i = 0; i < KMMIO_PAGE_TABLE_SIZE; i++) + INIT_LIST_HEAD(&kmmio_page_table[i]); + return 0; +} +__initcall(init_kmmio); + +EXPORT_SYMBOL_GPL(register_kmmio_probe); +EXPORT_SYMBOL_GPL(unregister_kmmio_probe); =================================================================== This BitKeeper patch contains the following changesets: 1.950,1.951 ## Wrapped with gzip_uu ## begin 664 bkpatch1305 M'XL(`(^.)SX``^U<ZW+;.++^+3X%9K*5(SFRS+LD:YR-XU$R+E_BLIVSWMU, ML2@2M+BB2!V22L:S\C[[Z09(B5?KDGAWIRI.RA8)H-'H_M#=:`!Z03Y&-#QL M>,'<C807Y)<@B@\;8_/+I!.-.ZX?4Z]C!5,HN0X"*#D8!U-ZP&H?R!UM?S*= MNH$`Q5=F;(W)9QI&APVIHRS?Q`\S>MBX'K[_>'Y\+0A'1^1D;/KW](;&Y.A( MB(/PL^G9T1LS'GN!WXE#TX^F-#:QU\6RZD(611G^:5)7$35](>FBVEU8DBU) MIBI16Y35GJX*C*\W)>[S=!11DKJBK*B:NM!TH";\3*1.7Q.)J!R(TH'4)9)T M*&J'4O^5V#L415)#EKR2R+XHO"7?=A`G@D4N:'A/!9>$\RA^>!,!P3BPS"BF M82>*77_R8)EQ!7%)EB115S49B*NJWA?.B*;)8E^X^&I*5RN]"?M;_@B":(K" MZS5R,D-K?.`J/?U@0D.?>@=0/(LZ5D9L*C"V4!6YJRT<4:)]LR>IMD+EGJ[7 M*>E)LAP*H`RQMU``$**P#82@G=1?<`%S"$DY""GBH:RM@U#_N2!T;-OD[.+B M]`.!!Y)\="-BDK-9&(PH?++M_<"/B!.$9.:9ENO?0RDK)(%/6`/3LF@4M<D\ M@E)&**3W+L+'8%._V6H3T[>QU6?7YA0LT_-&IC4ASMRW8A=&0V['T#/\GT?4 MF7N,#O8Z#7P71H[-[-`%XY'T1Z(9M5S'M1(F;#N$MQV`L]*79?7?"L;I],`Q MYUY<!**X@$FB2XN^W)6TOJ)V>Z.^*IO.!D#,DTS`)/=5;2&J6K\+S-71<'W+ MF]M@@%U__ML!TT!GG">B`U^Z)"^4O@1_^Z:IZHHTLD=?0[.[$/N:]A1CR=SB MK:TB1[JB]>"W;.MJCXYDK=?73:KN1*R[D.2>UENKP(3&A3FACNO1[,3I*_V% MJG<5:3&RM+YN]VW5I(HCV70=1UEJ6;5IHM27@:7?W=F,>F^8)/>G>F_2"<+[ MOZ>,_IJ!P)D5^(Y[SVTMT!+!JDF]A:SJO=X"U*=(/=$<]26MJXO6!I#*T,LR M)LM=7?DOMKR,2554>NKV3%:HEG.I*CJH1!I1VW%$3;%L4[3LS;FL4S),=EU] M8@J4"%7B5ULHNM@5%Z8M]M2N9-N*V+-MLQYZFY'5`8.*]A1[Z30WH^D^)U<U MT[6%)/;%[D*U-"J9(U76`7^VNMYZ/$U67X@]3=)8_%<W(`P'+^D7@K(_K*TE M_"]1Z[WTU^F`>?%\&"CW#]6U/EQ\#A]ND;=N?$;I#+PBBH14AMX'M7("7XF# M!%]96^6:E"1P1\3?>K*T@U=]3IU(V^L$?/+S1%;OB(1A-9MM'\A^^(7]!PD\ M(>CMI7DJ$AB!<+!'!++'@S?\\):"E8_)U/0?B!6`PW)"&.R$!W18H7G2(C@2 M<HZ"(7\;SV%\Y"<FIL[O[.G-4DJO.]#D0'B1S&&HQD(`[DHZX]>E$CZLJI)I M8,\]6E42S5S?"ZQ)9:MI90O/'.'[3^7^N6TI-YF!5JW*[F<AI=-9G"\"8W50 M)(3OQN[]>$JGO/,H-F.(0$%:Q)K-C_:E0?H*5E%S*R:,&X-%<L;,O*=DSYJ' M_-,1N?QX?E[=@`?8O"[[6*@\]R/WWJ?HL4!UO$UD?J:V01W/O(\&@`I4]2FH M,0SGLQ@B^9`2VXW,D0?-('2G?AP^$#,BZ'$5%O7[.!!>G]R;,66XPL@]'M,' M".JGINNO:,3C(`SF]V,2S&-X``++6)Y!!H7".8/Y8WLT;"9#G,4&+!`BLH>_ MVX6Q8!S?$OXI-`#6?Z'_`TQ#GUG.VF24]@?_+8^:(>/Q[<?WS=9^9#JT@[TW M$J4:";_-U@#TU4"4I:L2]B*CCGL:&T5]-9N?`]<F>RW&UT!HN`YI_I`V:A%@ M%#EE[#`RK"ERY@>`"1,6,S89/7!!,+ZP^MW='8G`4I,9]=F"B+<"K00@ZA"Q M!//W@;=GJRV/3G^`Y@3:S_W\(!J-^R`.H#_^"EX\PK"`PA&)IC/$#RZ7@M!P M;58]&7,"J]6@V9O">*&_,KB@51-5M_\Z>7Y)FK?OC(OCF[/%*?_;XAVAL%:= MO7Q)E@_[KT$_*3"X&*O+5NW;A.,EU<0C]I#C8W%$$CX&A9*71^1?IVG1IP1< M9!QXB#IKT@;I?2%?*(DHP!/6D3`YR`B,*&#OZG;(\(6+6_"PL*R=<8`AL,)I M&3$I.&`8"!$4Q"=D)YZ'/I&P^U15ARN44A]!:D`)]&Z-*5=5TD@<"(_0;+LI M+7W;*4U8RV-H^241'(<T,]O+^3X+HA1.J?[RTQO\ANTBP3:IL@9LYK,IYD8) M'1/Z_PS3MP406<F#52K#F_QPA),G7_?3&B`BS[5(S!1FH9@9!^.;X1$ZVA`1 MAN%X\VALQ-[("/Q*S)0!7(-M0'V5`_B4&@%T2D"N9#:>AIY`DA]`70,T#Q)$ MBS4*[`="/;!*``X^)7!&S%A6QPJ#*%JFAV8!(*)-.#^,QA?7\\@8N(1IBE.M MC?/KR]BUQF#I(B[5&#PR170FX$3"B8XQNX1D`H?8@6'3T?R^C6@'UE)W@$8W M[7_LQAVL?\#57[!8B3!+2%G-T\>L?X=``4844R,F>V@RX6.S[+B`R18!"*>R MF]W;V`#^@![P(7"<"`UN,ZV->ICR2E-6:9I6:D+]-LG4PT%@L6>&@"FLGYT3 MS82Y%KQ?60Y\F?;)XC*DD*7Z.&"&A5D6B'`/3I@_A69H_B(VK_,R8.[!\D*4 M@(%U*L301C>%CBI;`'6G9C1A\FDDS,*?`7M('!%*-37O4?*,S+2)L?QL0*3> MQ)8MT.*_EE1;@P8?3`6[":G_-+L+4N`6'0-CL-)J).8/Z$/?S"P(_X1YG!]` MBP4=8!VNCM\/4_.04T]%?>"/5;^Z'MX,+V];'.N,DSJG5L7,1KSD9+\Q+RRD MHDOO@U,[\+T'PL(I"':FY@1TZX:XS)GAB]$\0JSRX(4SZ_J?:1@;4"&):;"^ MP15>T/4\,I9Q)[HQ/F4&><4S-U(<PI(F@G$Y;IR7+!YEEI=3`\RD_>3J0I75 M^[SL\DBKZYEW4HF_9FF^K%A$.*;C)IE@8TGW52*%1O+#%#.\N_IP?6O<_/7B M[8=SX_W5>;,H;""%&9RZG$\^@U-7Z^D,SM>EJ?Z(&9Q:.9T1-DCAJEZ4UZ0D M@6?(X'P#G>R0P5&4YTS@L'1D(7]3*^:=\C>*DD3USY*TP>2(X]O4(<;QS87! M,D3&+\(+>(.^,?^RD!/!/?GHZ30*^MIL"(\F$IMAAVAQ]TC6:PPX.UAX\N'R MW>E[WK5`?X/UB4^V\8.#7*/-7=:RW;9+%8P/>$17N6C)T=TQY9$?T^XN;""\ M8.$Y>-`?LG(N1W*["*%1+P0(E$AFQ4K`793[VU$X%;1?8.;$2?[@:+-HQL%F MG5`N,UCM@7)5-G,_.VRPUO@>_8_@>_(2.B-LA!F+F"^_)J6Q/Z/7V54599>S M5A5Z]SE=#MM"KW$Y>0'OY&_T[LHEG)]>?KPK.X7"ZXWS_IX;Q57O84GN.BX- M*[/XTUDYP;Y,X"_]2R8_.1!J$NN#-<Z(-/=2,LO<HA&WFE5I]XRM*YJH3%&E M$:_N,I-$VJ!/LH,);@TJA(7)K.0M:L<84]-FG](<J!=8)EM?!0Y;;V42-RQO MDG.A^(<U1&M[8GJ8,!Q1)P@I*\(U&OV-6O.8VIU,ZYR\2>8I92(A93HQGNTI M4$)WX.&YGDZ.9DZ@)/LX2+,`U=LOVTJ$)^;YSDU1(!QVG_B2T0KF?IQT7A7F M`.WSP)I$G"7P.^'_D>D\BD&$J_QKNH+-9.KP&93+7N=2>$D!6ROS5"SF>UFR M%#<<V/9"!@O1GZLC@&*>E9'%=7`2CBRQN'+?R5!3?YQ[F2S>W].8B8\S9B;) M9*;<)KAKB');'4(N</PL'VAQ#*R&QYDM3Y/B=D4)H<6)D-UZJ]S@J0H3<:SP M9K/*3#4A]:@9T<U:?&(=Y`_))>.I&O-LI?^MVK!`<%.5;Q1BY7Q#&F0]=4)C MFW,9NQP/^R/&4Z4S&&QHX.=+)R]*HWV&"&IGJ>\0.DGJLYZWX(?^"L'3-SEE MH;)3%C?SV2P(8W8JE2VIN&'M/,OZ/5F^;WBVX:D#%&,3M%#QWO7=RH#MB6,: MR:&+?+QFF=:8LNVL<AD-0W_=&8HT\DS,RO'-+\;;T]L;HA=*;H_?G@^-F]._ M#4E3(C_]5&S06B7_2RZ>V[L8O>S?B]1^'9288%G2%2=J57$-._FFK34<H4/( ML54@S7BK<\'D"/>LT@X2!!AI%7R`&C=7IY?&^8>3,^/C)?X9_IS$#,5H)-H\ M','P`/MC?31?KOK+[B24@Y5E,UY4T1#8^FLPYR$!)O7Y3C-N`2Z#"3ZN#:,# MGC19IO9+2MC#WVVR%T]G+)!C+X_(RRQ<</H8LSAL\K1+$72@H08C"%;!H#`5 MFD"L39`2WTRN],[0"6O$=NQYBW*]-JO#3GBPO;_]URR`PN.1;$2-Y>;?C!^' M2)[X2:''[<.,=/LCI`FV&AD]&X"1@LH:&3"^>I5L419UD+#=XM+@I/>'P[O3 MF]OEV95@'O,1,*%`]>9+:(8/[1IE)%0K]-%:;OA71G!Y=F8AC'?2/!M>7QK# MZVORXT>V&8[@8T=!EB=Z/OD_IF=.D-O#')*K1+/:WX?0-GN8(Z1Q;I9LJ9\G M]5$;A::C3L%J4V\IXH(B]_<'ZP;W^,0":],@>Z/YN,'^8G[*9NSI"BILE['2 M.F\W>;.CG+&U^)I9O&I0GLK\S-E1(HS-IO*&2Y*,:,N,.VQ6;R!8I^Y$7'I" M!0?B<#$Y^Z]71B![2N>1TVG6LM.:3&%^!%8S<G^G@=/<<UIM\O[=E7%\^^'B M]&1YX@XZ2LBRXRQIAT`[>4S.\/$U><:..*D=`<:>0DE"H@XHW*I4[C,XJT,[ M^?-:VRX,U^EMLPFQD=Y`G@FS&6&RF>\Z4)B\X,JMVU_)C#MC5)R547G,'Z&) MB6%@S$GP5S$N8.O2-`GS[O3N8D@8+O#(VBHSQ736QL-;(<PY[X'PL,PFO!-H M\<#2-$`#.W%-#U"%A-CT8_.:'1-H8`C?=)F3(R[YJ11APMM7KW!:GEZ>WL*R M]^;6^&5X_',SYXS<7YFAKR15B.,X/5)+,(-(3C6+)"XV'%US)3H&R/)N?)4W M@:KEBM6.)]F\K[WOLOXZ[E=>P=G\AD#Y"HZLJ&IO(2N:I+`5JB+O<"7@^U7+ M;:Y:\OM.ZZXXI*K:9?6MJ+"V#T;_V/]3,YM/;9'LSZOD"&0GJ(9O<JEL!_1N M=<M-6'N!^DGZR7UJ^+60-$U+0-PK7CD7>_^1[!:"^.;DY(9,\=ZY+;@K^ICF MVD:0DBSCO6CXO5!4K:L"+1N6?/]X@YD3`.&$/N"ER,Y\LHZ.(HFJS';W$CI< M`_C;"L(9LM(QYY6W*ROO&XHP=K&K=1<J*$+^%O0D7=,5X$\11?$K9(:T^F#A M-!EH::`1H#6ZIV$4O[%=VX_MN55_D;3Z:J4BZ6"!Y84*\NOB53!VN?WBVY)= M9QJ2QL]ZE^S)R_QLCO6W=Q3J=T>QC:/@]W>?#PVG,JBN/E]ZJD$PH*2)@8I3 M+\7S_)F3U=*`%+S*ZF[^-AYEVR\)V`#@55\2P*,@799U_HT3LO(=W,\-;OQ" MAEILKY2T$ZZ5)W$MZ5W`-4-UZ<8,WG/)HWQU@0I/_Z\PCCF!S%;>YM'^3M^@ ML&XOJBZRAYFA2QS3VO?(_IDQS;^NHGHS[6OB>5!=73Q?&\4G7UR!<'RF;\_8 MP-:6OCTC,;1*%X(QMB&Z`RB_?[//=LM-]E4EM98V4=$NL(0%ETKZ0@*TY"Y] M\C,*`H_\R$78'-Y=#:]/+X:7M\?GK1^%ADWQ#B]>3R'9HES!V=7UA[?#&X+9 K:F\F-+Z1/AK?1AV-W;3Q:?6-;M:86I-H/CVR%+#[HFX+_P_:DR5D,TX````` ` end |