From fee4dd4e6d65f8c546f9529c3973dafa3f413ae3 Mon Sep 17 00:00:00 2001 From: Mercier Pierre-Olivier Date: Mon, 11 Feb 2013 22:04:30 +0100 Subject: [PATCH] Initial snapshot --- Makefile | 145 + boot/Makefile | 56 + boot/loader/Makefile | 47 + boot/loader/ibm-pc.ia32/educational/Makefile | 50 + boot/loader/ibm-pc.ia32/educational/loader | Bin 0 -> 20441 bytes environment/Makefile | 41 + .../__pycache__/critical.cpython-32.pyc | Bin 0 -> 7612 bytes environment/clean.py | 138 + environment/critical.py | 426 +++ environment/critical.pyc | Bin 0 -> 7117 bytes environment/initialize.py | 168 + environment/profile/boot/boot.conf | 76 + environment/profile/boot/boot.desc | 23 + environment/profile/environment.conf | 348 ++ environment/profile/host/darwin/darwin.conf | 113 + environment/profile/host/darwin/darwin.desc | 33 + environment/profile/host/darwin/darwin.mk | 493 +++ environment/profile/host/darwin/darwin.py | 226 ++ .../profile/host/darwin/ia32.ia32/educational | 1 + .../profile/host/darwin/ia32.ia32/ia32.conf | 33 + .../profile/host/darwin/ia32.ia32/ia32.desc | 1 + .../profile/host/darwin/ia32.ia32/ia32.mk | 1 + .../profile/host/darwin/ia32.ia32/ia32.py | 1 + .../profile/host/darwin/ia32.ia32/optimised | 1 + .../profile/host/darwin/ia32.null/null.conf | 33 + .../profile/host/darwin/ia32.null/null.desc | 1 + .../profile/host/darwin/ia32.null/null.mk | 1 + .../profile/host/darwin/ia32.null/null.py | 1 + environment/profile/host/host.conf | 33 + environment/profile/host/host.desc | 18 + environment/profile/host/host.mk | 214 ++ environment/profile/host/host.py | 382 ++ .../profile/host/linux/ia32.ia32/educational | 1 + .../profile/host/linux/ia32.ia32/ia32.conf | 1 + .../profile/host/linux/ia32.ia32/ia32.desc | 1 + .../profile/host/linux/ia32.ia32/ia32.mk | 1 + .../profile/host/linux/ia32.ia32/ia32.py | 1 + .../profile/host/linux/ia32.ia32/optimised | 1 + .../host/linux/ia32.mips64/mips64.conf | 137 + .../host/linux/ia32.mips64/mips64.desc | 33 + .../profile/host/linux/ia32.mips64/mips64.mk | 23 + .../profile/host/linux/ia32.mips64/mips64.py | 219 ++ .../profile/host/linux/ia32.mips64/util.py | 48 + .../profile/host/linux/ia32.null/null.conf | 33 + .../profile/host/linux/ia32.null/null.desc | 1 + .../profile/host/linux/ia32.null/null.mk | 1 + .../profile/host/linux/ia32.null/null.py | 1 + environment/profile/host/linux/linux.conf | 113 + environment/profile/host/linux/linux.desc | 33 + environment/profile/host/linux/linux.mk | 507 +++ environment/profile/host/linux/linux.py | 239 ++ environment/profile/kaneton/core/core.conf | 152 + environment/profile/kaneton/core/core.desc | 26 + environment/profile/kaneton/kaneton.conf | 34 + environment/profile/kaneton/kaneton.desc | 12 + .../profile/kaneton/library/library.conf | 24 + .../profile/kaneton/library/library.desc | 12 + .../profile/kaneton/machine/machine.conf | 24 + .../profile/kaneton/machine/machine.desc | 12 + .../machine/platform/qemu-mips/qemu-mips.conf | 12 + .../machine/platform/qemu-mips/qemu-mips.desc | 0 .../profile/kaneton/modules/modules.conf | 64 + .../profile/kaneton/modules/modules.desc | 12 + environment/profile/user/sample/sample.conf | 39 + environment/profile/user/test/test.conf | 33 + environment/profile/user/user.conf | 65 + environment/profile/user/user.desc | 26 + export/Makefile | 53 + export/behaviours/sets/chop.yml | 146 + export/behaviours/sets/export.yml | 23 + export/behaviours/sets/ibm-pc.ia32.yml | 68 + export/behaviours/sets/k0.yml | 31 + export/behaviours/sets/k1.yml | 52 + export/behaviours/sets/k2.yml | 62 + export/behaviours/sets/k3.yml | 40 + export/behaviours/sets/k4.yml | 8 + export/behaviours/sets/sweep.yml | 33 + export/behaviours/sets/user:sample.yml | 29 + export/behaviours/sets/user:test.yml | 29 + export/behaviours/snapshot.yml | 55 + export/behaviours/test:contributor.yml | 36 + export/behaviours/test:group.yml | 40 + export/behaviours/test:student.yml | 40 + export/data/snapshot/Makefile | 145 + .../loader/ibm-pc.ia32/educational/Makefile | 50 + .../loader/ibm-pc.ia32/educational/loader | Bin 0 -> 20441 bytes .../snapshot/kaneton/core/include/scheduler.h | 290 ++ .../kaneton/core/scheduler/scheduler-mfq.c | 463 +++ .../architecture/ia32/educational/context.c | 704 ++++ .../ia32/educational/environment.c | 444 +++ .../architecture/ia32/educational/handler.c | 24 + .../architecture/ia32/educational/idt.c | 34 + .../ia32/educational/include/context.h | 117 + .../ia32/educational/include/handler.h | 56 + .../ia32/educational/include/idt.h | 121 + .../ia32/educational/include/paging.h | 83 + .../ia32/educational/include/pd.h | 49 + .../ia32/educational/include/pt.h | 48 + .../architecture/ia32/educational/paging.c | 545 +++ .../architecture/ia32/educational/pd.c | 30 + .../architecture/ia32/educational/pt.c | 32 + .../machine/glue/ibm-pc.ia32/educational/as.c | 88 + .../glue/ibm-pc.ia32/educational/event.c | 45 + .../glue/ibm-pc.ia32/educational/include/as.h | 90 + .../ibm-pc.ia32/educational/include/event.h | 83 + .../glue/ibm-pc.ia32/educational/region.c | 49 + .../glue/ibm-pc.ia32/educational/scheduler.c | 219 ++ export/data/snapshot/test/Makefile | 60 + export/data/snapshot/tool/Makefile | 50 + export/export.py | 137 + export/modules/Makefile | 37 + export/modules/bremove.py | 72 + export/modules/breplace.py | 73 + export/modules/fmove.py | 45 + export/modules/fremove.py | 45 + export/modules/fremovepattern.py | 46 + export/modules/freplace.py | 45 + export/modules/initenv.py | 46 + export/modules/localexport.py | 46 + export/modules/mkdir.py | 45 + export/modules/svnexport.py | 46 + export/modules/symlink.py | 45 + export/modules/tarball.py | 49 + kaneton/Makefile | 83 + kaneton/core/Makefile | 148 + kaneton/core/as/Makefile | 76 + kaneton/core/as/as.c | 1364 +++++++ kaneton/core/capability/Makefile | 76 + kaneton/core/capability/capability.c | 611 ++++ kaneton/core/clock/Makefile | 76 + kaneton/core/clock/clock.c | 217 ++ kaneton/core/core.c | 318 ++ kaneton/core/cpu/Makefile | 76 + kaneton/core/cpu/cpu.c | 548 +++ kaneton/core/event/Makefile | 76 + kaneton/core/event/event.c | 574 +++ kaneton/core/id/Makefile | 76 + kaneton/core/id/id.c | 222 ++ kaneton/core/include/as.h | 165 + kaneton/core/include/bpt.h | 3138 +++++++++++++++++ kaneton/core/include/capability.h | 143 + kaneton/core/include/clock.h | 138 + kaneton/core/include/core.h | 146 + kaneton/core/include/cpu.h | 118 + kaneton/core/include/error.h | 75 + kaneton/core/include/event.h | 193 + kaneton/core/include/id.h | 128 + kaneton/core/include/init.h | 201 ++ kaneton/core/include/interface.h | 639 ++++ kaneton/core/include/io.h | 129 + kaneton/core/include/kernel.h | 97 + kaneton/core/include/map.h | 108 + kaneton/core/include/message.h | 246 ++ kaneton/core/include/region.h | 285 ++ kaneton/core/include/scheduler.h | 290 ++ kaneton/core/include/segment.h | 304 ++ kaneton/core/include/set-array.h | 49 + kaneton/core/include/set-bpt.h | 106 + kaneton/core/include/set-ll.h | 58 + kaneton/core/include/set.h | 753 ++++ kaneton/core/include/task.h | 305 ++ kaneton/core/include/thread.h | 335 ++ kaneton/core/include/timer.h | 232 ++ kaneton/core/include/types.h | 58 + kaneton/core/include/wait.h | 123 + kaneton/core/interface/Makefile | 76 + kaneton/core/interface/interface.c | 1055 ++++++ kaneton/core/io/Makefile | 76 + kaneton/core/io/io.c | 210 ++ kaneton/core/kernel/Makefile | 76 + kaneton/core/kernel/kernel.c | 471 +++ kaneton/core/map/Makefile | 76 + kaneton/core/map/map.c | 481 +++ kaneton/core/message/Makefile | 76 + kaneton/core/message/message.c | 1029 ++++++ kaneton/core/region/Makefile | 76 + kaneton/core/region/region-fit.c | 1627 +++++++++ kaneton/core/scheduler/Makefile | 76 + kaneton/core/scheduler/scheduler-mfq.c | 463 +++ kaneton/core/segment/Makefile | 76 + kaneton/core/segment/segment-fit.c | 2132 +++++++++++ kaneton/core/set/Makefile | 81 + kaneton/core/set/set-array.c | 1703 +++++++++ kaneton/core/set/set-bpt.c | 1345 +++++++ kaneton/core/set/set-ll.c | 1434 ++++++++ kaneton/core/set/set-pipe.c | 391 ++ kaneton/core/set/set-stack.c | 386 ++ kaneton/core/set/set.c | 592 ++++ kaneton/core/task/Makefile | 76 + kaneton/core/task/task.c | 2410 +++++++++++++ kaneton/core/thread/Makefile | 76 + kaneton/core/thread/thread.c | 1953 ++++++++++ kaneton/core/timer/Makefile | 76 + kaneton/core/timer/timer.c | 1207 +++++++ kaneton/include/architecture | 1 + kaneton/include/core | 1 + kaneton/include/glue | 1 + kaneton/include/kaneton.h | 36 + kaneton/include/library | 1 + kaneton/include/machine | 1 + kaneton/include/modules | 1 + kaneton/include/platform | 1 + kaneton/library/Makefile | 94 + kaneton/library/alloc.c | 667 ++++ kaneton/library/ctype.c | 117 + kaneton/library/format.c | 422 +++ kaneton/library/include/alloc.h | 101 + kaneton/library/include/ctype.h | 66 + kaneton/library/include/format.h | 64 + kaneton/library/include/library.h | 280 ++ kaneton/library/include/limits.h | 104 + kaneton/library/include/stdarg.h | 37 + kaneton/library/include/types.h | 82 + kaneton/library/memcmp.c | 39 + kaneton/library/memcpy.c | 41 + kaneton/library/memset.c | 33 + kaneton/library/printf.c | 44 + kaneton/library/random.c | 58 + kaneton/library/snprintf.c | 64 + kaneton/library/sprintf.c | 59 + kaneton/library/strchr.c | 32 + kaneton/library/strcmp.c | 36 + kaneton/library/strcpy.c | 32 + kaneton/library/strdup.c | 32 + kaneton/library/strlen.c | 30 + kaneton/library/strncmp.c | 40 + kaneton/library/strncpy.c | 35 + kaneton/library/strrchr.c | 34 + kaneton/library/strtol.c | 140 + kaneton/library/strtoul.c | 111 + kaneton/library/sum2.c | 46 + kaneton/machine/Makefile | 79 + .../architecture/ia32/educational/Makefile | 102 + .../ia32/educational/architecture.c | 66 + .../architecture/ia32/educational/context.c | 704 ++++ .../ia32/educational/environment.c | 444 +++ .../architecture/ia32/educational/gdt.c | 531 +++ .../architecture/ia32/educational/handler.c | 24 + .../architecture/ia32/educational/idt.c | 34 + .../ia32/educational/include/architecture.h | 182 + .../ia32/educational/include/asm.h | 120 + .../ia32/educational/include/context.h | 117 + .../ia32/educational/include/environment.h | 36 + .../ia32/educational/include/gdt.h | 281 ++ .../ia32/educational/include/handler.h | 56 + .../ia32/educational/include/idt.h | 121 + .../ia32/educational/include/io.h | 86 + .../ia32/educational/include/linker.h | 52 + .../ia32/educational/include/paging.h | 83 + .../ia32/educational/include/pd.h | 49 + .../ia32/educational/include/pmode.h | 39 + .../ia32/educational/include/privilege.h | 45 + .../ia32/educational/include/pt.h | 48 + .../ia32/educational/include/register.h | 89 + .../ia32/educational/include/segmentation.h | 34 + .../ia32/educational/include/tlb.h | 36 + .../ia32/educational/include/tss.h | 126 + .../ia32/educational/include/types.h | 53 + .../architecture/ia32/educational/io.c | 297 ++ .../architecture/ia32/educational/paging.c | 545 +++ .../architecture/ia32/educational/pd.c | 30 + .../architecture/ia32/educational/pmode.c | 127 + .../architecture/ia32/educational/pt.c | 32 + .../ia32/educational/segmentation.c | 183 + .../architecture/ia32/educational/tlb.c | 58 + .../architecture/ia32/educational/tss.c | 158 + .../glue/ibm-pc.ia32/educational/Makefile | 100 + .../machine/glue/ibm-pc.ia32/educational/as.c | 88 + .../glue/ibm-pc.ia32/educational/clock.c | 141 + .../glue/ibm-pc.ia32/educational/cpu.c | 74 + .../glue/ibm-pc.ia32/educational/event.c | 45 + .../glue/ibm-pc.ia32/educational/include/as.h | 90 + .../ibm-pc.ia32/educational/include/clock.h | 80 + .../ibm-pc.ia32/educational/include/cpu.h | 73 + .../ibm-pc.ia32/educational/include/event.h | 83 + .../ibm-pc.ia32/educational/include/glue.h | 44 + .../ibm-pc.ia32/educational/include/init.h | 33 + .../glue/ibm-pc.ia32/educational/include/io.h | 85 + .../ibm-pc.ia32/educational/include/kernel.h | 65 + .../ibm-pc.ia32/educational/include/map.h | 71 + .../ibm-pc.ia32/educational/include/message.h | 77 + .../ibm-pc.ia32/educational/include/region.h | 90 + .../educational/include/scheduler.h | 101 + .../ibm-pc.ia32/educational/include/segment.h | 95 + .../ibm-pc.ia32/educational/include/task.h | 96 + .../ibm-pc.ia32/educational/include/thread.h | 172 + .../ibm-pc.ia32/educational/include/timer.h | 76 + .../machine/glue/ibm-pc.ia32/educational/io.c | 173 + .../glue/ibm-pc.ia32/educational/kernel.c | 39 + .../ibm-pc.ia32/educational/layout/driver.lyt | 30 + .../ibm-pc.ia32/educational/layout/guest.lyt | 30 + .../ibm-pc.ia32/educational/layout/kernel.lyt | 37 + .../educational/layout/service.lyt | 30 + .../glue/ibm-pc.ia32/educational/map.c | 41 + .../glue/ibm-pc.ia32/educational/message.c | 50 + .../glue/ibm-pc.ia32/educational/region.c | 49 + .../glue/ibm-pc.ia32/educational/scheduler.c | 219 ++ .../glue/ibm-pc.ia32/educational/segment.c | 189 + .../glue/ibm-pc.ia32/educational/task.c | 131 + .../glue/ibm-pc.ia32/educational/thread.c | 352 ++ .../glue/ibm-pc.ia32/educational/timer.c | 112 + kaneton/machine/include/machine.h | 157 + kaneton/machine/include/types.h | 19 + kaneton/machine/platform/ibm-pc/Makefile | 82 + kaneton/machine/platform/ibm-pc/console.c | 258 ++ .../machine/platform/ibm-pc/include/console.h | 108 + kaneton/machine/platform/ibm-pc/include/pic.h | 75 + kaneton/machine/platform/ibm-pc/include/pit.h | 57 + .../platform/ibm-pc/include/platform.h | 37 + kaneton/machine/platform/ibm-pc/include/rtc.h | 117 + .../machine/platform/ibm-pc/include/serial.h | 84 + kaneton/machine/platform/ibm-pc/pic.c | 254 ++ kaneton/machine/platform/ibm-pc/pit.c | 87 + kaneton/machine/platform/ibm-pc/rtc.c | 309 ++ kaneton/machine/platform/ibm-pc/serial.c | 199 ++ kaneton/modules/Makefile | 84 + kaneton/modules/bundle/Makefile | 68 + kaneton/modules/console/Makefile | 78 + kaneton/modules/console/console.c | 274 ++ kaneton/modules/console/include/console.h | 132 + kaneton/modules/forward/Makefile | 79 + kaneton/modules/forward/forward.c | 215 ++ kaneton/modules/forward/include/forward.h | 57 + kaneton/modules/modules.c | 12 + kaneton/modules/modules.h | 115 + kaneton/modules/report/Makefile | 79 + kaneton/modules/report/include/report.h | 65 + kaneton/modules/report/report.c | 171 + kaneton/modules/test/Makefile | 79 + kaneton/modules/test/include/test.h | 121 + kaneton/modules/test/test.c | 531 +++ license/Makefile | 37 + license/kaneton.txt | 10 + license/pedagogical.txt | 159 + sample/Makefile | 47 + sample/chiche/Makefile | 76 + sample/chiche/_crt.S | 70 + sample/chiche/_kayou.c | 38 + sample/chiche/chiche.c | 26 + sample/chiche/chiche.h | 32 + test/Makefile | 60 + test/README | 15 + test/client/Makefile | 61 + test/client/README | 165 + test/client/client.py | 371 ++ test/packages/Makefile | 43 + test/packages/ktp/Makefile | 33 + test/packages/ktp/__init__.py | 58 + test/packages/ktp/bulletin.py | 41 + test/packages/ktp/capability.py | 111 + test/packages/ktp/certificate.py | 84 + test/packages/ktp/code.py | 55 + test/packages/ktp/configuration.py | 34 + test/packages/ktp/database.py | 68 + test/packages/ktp/environment.py | 65 + test/packages/ktp/hook.py | 37 + test/packages/ktp/key.py | 47 + test/packages/ktp/log.py | 50 + test/packages/ktp/manifest.py | 34 + test/packages/ktp/miscellaneous.py | 282 ++ test/packages/ktp/process.py | 173 + test/packages/ktp/queue.py | 59 + test/packages/ktp/report.py | 67 + test/packages/ktp/snapshot.py | 18 + test/packages/ktp/stage.py | 62 + test/packages/ktp/statement.py | 62 + test/packages/ktp/suite.py | 106 + test/packages/ktp/trace.py | 46 + test/packages/ktp/xmlrpc.py | 60 + tool/Makefile | 50 + tool/mbl/grub/data/kaneton.img | Bin 0 -> 1474535 bytes tool/mbl/grub/grub.py | 284 ++ tool/mkp/mkp.py | 239 ++ 373 files changed, 62144 insertions(+) create mode 100644 Makefile create mode 100644 boot/Makefile create mode 100644 boot/loader/Makefile create mode 100644 boot/loader/ibm-pc.ia32/educational/Makefile create mode 100755 boot/loader/ibm-pc.ia32/educational/loader create mode 100644 environment/Makefile create mode 100644 environment/__pycache__/critical.cpython-32.pyc create mode 100644 environment/clean.py create mode 100644 environment/critical.py create mode 100644 environment/critical.pyc create mode 100644 environment/initialize.py create mode 100644 environment/profile/boot/boot.conf create mode 100644 environment/profile/boot/boot.desc create mode 100644 environment/profile/environment.conf create mode 100644 environment/profile/host/darwin/darwin.conf create mode 100644 environment/profile/host/darwin/darwin.desc create mode 100644 environment/profile/host/darwin/darwin.mk create mode 100644 environment/profile/host/darwin/darwin.py create mode 120000 environment/profile/host/darwin/ia32.ia32/educational create mode 100644 environment/profile/host/darwin/ia32.ia32/ia32.conf create mode 120000 environment/profile/host/darwin/ia32.ia32/ia32.desc create mode 120000 environment/profile/host/darwin/ia32.ia32/ia32.mk create mode 120000 environment/profile/host/darwin/ia32.ia32/ia32.py create mode 120000 environment/profile/host/darwin/ia32.ia32/optimised create mode 100644 environment/profile/host/darwin/ia32.null/null.conf create mode 120000 environment/profile/host/darwin/ia32.null/null.desc create mode 120000 environment/profile/host/darwin/ia32.null/null.mk create mode 120000 environment/profile/host/darwin/ia32.null/null.py create mode 100644 environment/profile/host/host.conf create mode 100644 environment/profile/host/host.desc create mode 100644 environment/profile/host/host.mk create mode 100644 environment/profile/host/host.py create mode 120000 environment/profile/host/linux/ia32.ia32/educational create mode 120000 environment/profile/host/linux/ia32.ia32/ia32.conf create mode 120000 environment/profile/host/linux/ia32.ia32/ia32.desc create mode 120000 environment/profile/host/linux/ia32.ia32/ia32.mk create mode 120000 environment/profile/host/linux/ia32.ia32/ia32.py create mode 120000 environment/profile/host/linux/ia32.ia32/optimised create mode 100644 environment/profile/host/linux/ia32.mips64/mips64.conf create mode 100644 environment/profile/host/linux/ia32.mips64/mips64.desc create mode 100644 environment/profile/host/linux/ia32.mips64/mips64.mk create mode 100644 environment/profile/host/linux/ia32.mips64/mips64.py create mode 100644 environment/profile/host/linux/ia32.mips64/util.py create mode 100644 environment/profile/host/linux/ia32.null/null.conf create mode 120000 environment/profile/host/linux/ia32.null/null.desc create mode 120000 environment/profile/host/linux/ia32.null/null.mk create mode 120000 environment/profile/host/linux/ia32.null/null.py create mode 100644 environment/profile/host/linux/linux.conf create mode 100644 environment/profile/host/linux/linux.desc create mode 100644 environment/profile/host/linux/linux.mk create mode 100644 environment/profile/host/linux/linux.py create mode 100644 environment/profile/kaneton/core/core.conf create mode 100644 environment/profile/kaneton/core/core.desc create mode 100644 environment/profile/kaneton/kaneton.conf create mode 100644 environment/profile/kaneton/kaneton.desc create mode 100644 environment/profile/kaneton/library/library.conf create mode 100644 environment/profile/kaneton/library/library.desc create mode 100644 environment/profile/kaneton/machine/machine.conf create mode 100644 environment/profile/kaneton/machine/machine.desc create mode 100755 environment/profile/kaneton/machine/platform/qemu-mips/qemu-mips.conf create mode 100755 environment/profile/kaneton/machine/platform/qemu-mips/qemu-mips.desc create mode 100644 environment/profile/kaneton/modules/modules.conf create mode 100644 environment/profile/kaneton/modules/modules.desc create mode 100644 environment/profile/user/sample/sample.conf create mode 100644 environment/profile/user/test/test.conf create mode 100644 environment/profile/user/user.conf create mode 100644 environment/profile/user/user.desc create mode 100644 export/Makefile create mode 100644 export/behaviours/sets/chop.yml create mode 100644 export/behaviours/sets/export.yml create mode 100644 export/behaviours/sets/ibm-pc.ia32.yml create mode 100644 export/behaviours/sets/k0.yml create mode 100644 export/behaviours/sets/k1.yml create mode 100644 export/behaviours/sets/k2.yml create mode 100644 export/behaviours/sets/k3.yml create mode 100644 export/behaviours/sets/k4.yml create mode 100644 export/behaviours/sets/sweep.yml create mode 100644 export/behaviours/sets/user:sample.yml create mode 100644 export/behaviours/sets/user:test.yml create mode 100644 export/behaviours/snapshot.yml create mode 100644 export/behaviours/test:contributor.yml create mode 100644 export/behaviours/test:group.yml create mode 100644 export/behaviours/test:student.yml create mode 100644 export/data/snapshot/Makefile create mode 100644 export/data/snapshot/boot/loader/ibm-pc.ia32/educational/Makefile create mode 100755 export/data/snapshot/boot/loader/ibm-pc.ia32/educational/loader create mode 100644 export/data/snapshot/kaneton/core/include/scheduler.h create mode 100644 export/data/snapshot/kaneton/core/scheduler/scheduler-mfq.c create mode 100644 export/data/snapshot/kaneton/machine/architecture/ia32/educational/context.c create mode 100644 export/data/snapshot/kaneton/machine/architecture/ia32/educational/environment.c create mode 100644 export/data/snapshot/kaneton/machine/architecture/ia32/educational/handler.c create mode 100644 export/data/snapshot/kaneton/machine/architecture/ia32/educational/idt.c create mode 100644 export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/context.h create mode 100644 export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/handler.h create mode 100644 export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/idt.h create mode 100644 export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/paging.h create mode 100644 export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/pd.h create mode 100644 export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/pt.h create mode 100644 export/data/snapshot/kaneton/machine/architecture/ia32/educational/paging.c create mode 100644 export/data/snapshot/kaneton/machine/architecture/ia32/educational/pd.c create mode 100644 export/data/snapshot/kaneton/machine/architecture/ia32/educational/pt.c create mode 100644 export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/as.c create mode 100644 export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/event.c create mode 100644 export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/include/as.h create mode 100644 export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/include/event.h create mode 100644 export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/region.c create mode 100644 export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/scheduler.c create mode 100644 export/data/snapshot/test/Makefile create mode 100644 export/data/snapshot/tool/Makefile create mode 100644 export/export.py create mode 100644 export/modules/Makefile create mode 100644 export/modules/bremove.py create mode 100644 export/modules/breplace.py create mode 100644 export/modules/fmove.py create mode 100644 export/modules/fremove.py create mode 100644 export/modules/fremovepattern.py create mode 100644 export/modules/freplace.py create mode 100644 export/modules/initenv.py create mode 100644 export/modules/localexport.py create mode 100644 export/modules/mkdir.py create mode 100644 export/modules/svnexport.py create mode 100644 export/modules/symlink.py create mode 100644 export/modules/tarball.py create mode 100644 kaneton/Makefile create mode 100644 kaneton/core/Makefile create mode 100644 kaneton/core/as/Makefile create mode 100644 kaneton/core/as/as.c create mode 100644 kaneton/core/capability/Makefile create mode 100644 kaneton/core/capability/capability.c create mode 100644 kaneton/core/clock/Makefile create mode 100644 kaneton/core/clock/clock.c create mode 100644 kaneton/core/core.c create mode 100644 kaneton/core/cpu/Makefile create mode 100644 kaneton/core/cpu/cpu.c create mode 100644 kaneton/core/event/Makefile create mode 100644 kaneton/core/event/event.c create mode 100644 kaneton/core/id/Makefile create mode 100644 kaneton/core/id/id.c create mode 100644 kaneton/core/include/as.h create mode 100644 kaneton/core/include/bpt.h create mode 100644 kaneton/core/include/capability.h create mode 100644 kaneton/core/include/clock.h create mode 100644 kaneton/core/include/core.h create mode 100644 kaneton/core/include/cpu.h create mode 100644 kaneton/core/include/error.h create mode 100644 kaneton/core/include/event.h create mode 100644 kaneton/core/include/id.h create mode 100644 kaneton/core/include/init.h create mode 100644 kaneton/core/include/interface.h create mode 100644 kaneton/core/include/io.h create mode 100644 kaneton/core/include/kernel.h create mode 100644 kaneton/core/include/map.h create mode 100644 kaneton/core/include/message.h create mode 100644 kaneton/core/include/region.h create mode 100644 kaneton/core/include/scheduler.h create mode 100644 kaneton/core/include/segment.h create mode 100644 kaneton/core/include/set-array.h create mode 100644 kaneton/core/include/set-bpt.h create mode 100644 kaneton/core/include/set-ll.h create mode 100644 kaneton/core/include/set.h create mode 100644 kaneton/core/include/task.h create mode 100644 kaneton/core/include/thread.h create mode 100644 kaneton/core/include/timer.h create mode 100644 kaneton/core/include/types.h create mode 100644 kaneton/core/include/wait.h create mode 100644 kaneton/core/interface/Makefile create mode 100644 kaneton/core/interface/interface.c create mode 100644 kaneton/core/io/Makefile create mode 100644 kaneton/core/io/io.c create mode 100644 kaneton/core/kernel/Makefile create mode 100644 kaneton/core/kernel/kernel.c create mode 100644 kaneton/core/map/Makefile create mode 100644 kaneton/core/map/map.c create mode 100644 kaneton/core/message/Makefile create mode 100644 kaneton/core/message/message.c create mode 100644 kaneton/core/region/Makefile create mode 100644 kaneton/core/region/region-fit.c create mode 100644 kaneton/core/scheduler/Makefile create mode 100644 kaneton/core/scheduler/scheduler-mfq.c create mode 100644 kaneton/core/segment/Makefile create mode 100644 kaneton/core/segment/segment-fit.c create mode 100644 kaneton/core/set/Makefile create mode 100644 kaneton/core/set/set-array.c create mode 100644 kaneton/core/set/set-bpt.c create mode 100644 kaneton/core/set/set-ll.c create mode 100644 kaneton/core/set/set-pipe.c create mode 100644 kaneton/core/set/set-stack.c create mode 100644 kaneton/core/set/set.c create mode 100644 kaneton/core/task/Makefile create mode 100644 kaneton/core/task/task.c create mode 100644 kaneton/core/thread/Makefile create mode 100644 kaneton/core/thread/thread.c create mode 100644 kaneton/core/timer/Makefile create mode 100644 kaneton/core/timer/timer.c create mode 120000 kaneton/include/architecture create mode 120000 kaneton/include/core create mode 120000 kaneton/include/glue create mode 100644 kaneton/include/kaneton.h create mode 120000 kaneton/include/library create mode 120000 kaneton/include/machine create mode 120000 kaneton/include/modules create mode 120000 kaneton/include/platform create mode 100644 kaneton/library/Makefile create mode 100644 kaneton/library/alloc.c create mode 100644 kaneton/library/ctype.c create mode 100644 kaneton/library/format.c create mode 100644 kaneton/library/include/alloc.h create mode 100644 kaneton/library/include/ctype.h create mode 100644 kaneton/library/include/format.h create mode 100644 kaneton/library/include/library.h create mode 100644 kaneton/library/include/limits.h create mode 100644 kaneton/library/include/stdarg.h create mode 100644 kaneton/library/include/types.h create mode 100644 kaneton/library/memcmp.c create mode 100644 kaneton/library/memcpy.c create mode 100644 kaneton/library/memset.c create mode 100644 kaneton/library/printf.c create mode 100644 kaneton/library/random.c create mode 100644 kaneton/library/snprintf.c create mode 100644 kaneton/library/sprintf.c create mode 100644 kaneton/library/strchr.c create mode 100644 kaneton/library/strcmp.c create mode 100644 kaneton/library/strcpy.c create mode 100644 kaneton/library/strdup.c create mode 100644 kaneton/library/strlen.c create mode 100644 kaneton/library/strncmp.c create mode 100644 kaneton/library/strncpy.c create mode 100644 kaneton/library/strrchr.c create mode 100644 kaneton/library/strtol.c create mode 100644 kaneton/library/strtoul.c create mode 100644 kaneton/library/sum2.c create mode 100644 kaneton/machine/Makefile create mode 100644 kaneton/machine/architecture/ia32/educational/Makefile create mode 100644 kaneton/machine/architecture/ia32/educational/architecture.c create mode 100644 kaneton/machine/architecture/ia32/educational/context.c create mode 100644 kaneton/machine/architecture/ia32/educational/environment.c create mode 100644 kaneton/machine/architecture/ia32/educational/gdt.c create mode 100644 kaneton/machine/architecture/ia32/educational/handler.c create mode 100644 kaneton/machine/architecture/ia32/educational/idt.c create mode 100644 kaneton/machine/architecture/ia32/educational/include/architecture.h create mode 100644 kaneton/machine/architecture/ia32/educational/include/asm.h create mode 100644 kaneton/machine/architecture/ia32/educational/include/context.h create mode 100644 kaneton/machine/architecture/ia32/educational/include/environment.h create mode 100644 kaneton/machine/architecture/ia32/educational/include/gdt.h create mode 100644 kaneton/machine/architecture/ia32/educational/include/handler.h create mode 100644 kaneton/machine/architecture/ia32/educational/include/idt.h create mode 100644 kaneton/machine/architecture/ia32/educational/include/io.h create mode 100644 kaneton/machine/architecture/ia32/educational/include/linker.h create mode 100644 kaneton/machine/architecture/ia32/educational/include/paging.h create mode 100644 kaneton/machine/architecture/ia32/educational/include/pd.h create mode 100644 kaneton/machine/architecture/ia32/educational/include/pmode.h create mode 100644 kaneton/machine/architecture/ia32/educational/include/privilege.h create mode 100644 kaneton/machine/architecture/ia32/educational/include/pt.h create mode 100644 kaneton/machine/architecture/ia32/educational/include/register.h create mode 100644 kaneton/machine/architecture/ia32/educational/include/segmentation.h create mode 100644 kaneton/machine/architecture/ia32/educational/include/tlb.h create mode 100644 kaneton/machine/architecture/ia32/educational/include/tss.h create mode 100644 kaneton/machine/architecture/ia32/educational/include/types.h create mode 100644 kaneton/machine/architecture/ia32/educational/io.c create mode 100644 kaneton/machine/architecture/ia32/educational/paging.c create mode 100644 kaneton/machine/architecture/ia32/educational/pd.c create mode 100644 kaneton/machine/architecture/ia32/educational/pmode.c create mode 100644 kaneton/machine/architecture/ia32/educational/pt.c create mode 100644 kaneton/machine/architecture/ia32/educational/segmentation.c create mode 100644 kaneton/machine/architecture/ia32/educational/tlb.c create mode 100644 kaneton/machine/architecture/ia32/educational/tss.c create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/Makefile create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/as.c create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/clock.c create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/cpu.c create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/event.c create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/include/as.h create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/include/clock.h create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/include/cpu.h create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/include/event.h create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/include/glue.h create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/include/init.h create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/include/io.h create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/include/kernel.h create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/include/map.h create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/include/message.h create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/include/region.h create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/include/scheduler.h create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/include/segment.h create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/include/task.h create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/include/thread.h create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/include/timer.h create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/io.c create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/kernel.c create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/layout/driver.lyt create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/layout/guest.lyt create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/layout/kernel.lyt create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/layout/service.lyt create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/map.c create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/message.c create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/region.c create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/scheduler.c create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/segment.c create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/task.c create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/thread.c create mode 100644 kaneton/machine/glue/ibm-pc.ia32/educational/timer.c create mode 100644 kaneton/machine/include/machine.h create mode 100644 kaneton/machine/include/types.h create mode 100644 kaneton/machine/platform/ibm-pc/Makefile create mode 100644 kaneton/machine/platform/ibm-pc/console.c create mode 100644 kaneton/machine/platform/ibm-pc/include/console.h create mode 100644 kaneton/machine/platform/ibm-pc/include/pic.h create mode 100644 kaneton/machine/platform/ibm-pc/include/pit.h create mode 100644 kaneton/machine/platform/ibm-pc/include/platform.h create mode 100644 kaneton/machine/platform/ibm-pc/include/rtc.h create mode 100644 kaneton/machine/platform/ibm-pc/include/serial.h create mode 100644 kaneton/machine/platform/ibm-pc/pic.c create mode 100644 kaneton/machine/platform/ibm-pc/pit.c create mode 100644 kaneton/machine/platform/ibm-pc/rtc.c create mode 100644 kaneton/machine/platform/ibm-pc/serial.c create mode 100644 kaneton/modules/Makefile create mode 100644 kaneton/modules/bundle/Makefile create mode 100644 kaneton/modules/console/Makefile create mode 100644 kaneton/modules/console/console.c create mode 100644 kaneton/modules/console/include/console.h create mode 100644 kaneton/modules/forward/Makefile create mode 100644 kaneton/modules/forward/forward.c create mode 100644 kaneton/modules/forward/include/forward.h create mode 100644 kaneton/modules/modules.c create mode 100644 kaneton/modules/modules.h create mode 100644 kaneton/modules/report/Makefile create mode 100644 kaneton/modules/report/include/report.h create mode 100644 kaneton/modules/report/report.c create mode 100644 kaneton/modules/test/Makefile create mode 100644 kaneton/modules/test/include/test.h create mode 100644 kaneton/modules/test/test.c create mode 100644 license/Makefile create mode 100644 license/kaneton.txt create mode 100644 license/pedagogical.txt create mode 100644 sample/Makefile create mode 100644 sample/chiche/Makefile create mode 100644 sample/chiche/_crt.S create mode 100644 sample/chiche/_kayou.c create mode 100644 sample/chiche/chiche.c create mode 100644 sample/chiche/chiche.h create mode 100644 test/Makefile create mode 100644 test/README create mode 100644 test/client/Makefile create mode 100644 test/client/README create mode 100644 test/client/client.py create mode 100644 test/packages/Makefile create mode 100644 test/packages/ktp/Makefile create mode 100644 test/packages/ktp/__init__.py create mode 100644 test/packages/ktp/bulletin.py create mode 100644 test/packages/ktp/capability.py create mode 100644 test/packages/ktp/certificate.py create mode 100644 test/packages/ktp/code.py create mode 100644 test/packages/ktp/configuration.py create mode 100644 test/packages/ktp/database.py create mode 100644 test/packages/ktp/environment.py create mode 100644 test/packages/ktp/hook.py create mode 100644 test/packages/ktp/key.py create mode 100644 test/packages/ktp/log.py create mode 100644 test/packages/ktp/manifest.py create mode 100644 test/packages/ktp/miscellaneous.py create mode 100644 test/packages/ktp/process.py create mode 100644 test/packages/ktp/queue.py create mode 100644 test/packages/ktp/report.py create mode 100644 test/packages/ktp/snapshot.py create mode 100644 test/packages/ktp/stage.py create mode 100644 test/packages/ktp/statement.py create mode 100644 test/packages/ktp/suite.py create mode 100644 test/packages/ktp/trace.py create mode 100644 test/packages/ktp/xmlrpc.py create mode 100644 tool/Makefile create mode 100644 tool/mbl/grub/data/kaneton.img create mode 100644 tool/mbl/grub/grub.py create mode 100644 tool/mkp/mkp.py diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8fb8b5d --- /dev/null +++ b/Makefile @@ -0,0 +1,145 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kanetno +# +# file /home/mycure/kaneton/export/data/snapshot/Makefile +# +# created julien quintard [tue jun 26 11:27:22 2007] +# updated julien quintard [sat feb 5 12:11:16 2011] +# + +# +# ---------- dependencies ----------------------------------------------------- +# + +-include environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.SILENT: + +.PHONY: main initialize clean clear prototypes \ + headers build install info + +# +# ---------- variables -------------------------------------------------------- +# + +_PYTHON_ ?= $(KANETON_PYTHON) +_MAKE_ ?= $(MAKE) + +# +# ---------- default rule ----------------------------------------------------- +# + +ifeq ($(_SIGNATURE_),kaneton) + +PATHS = $(dir $(_COMPONENTS_)) + + main: + for path in $(PATHS) ; do \ + if [ -f "$${path}/Makefile" ] ; then \ + $(call env_launch,$${path}/Makefile,,) ; \ + fi \ + done + +else + + main \ + clear \ + prototypes \ + headers \ + build install \ + info \ + clean: initialize + $(_MAKE_) -f Makefile $@ + +endif + +# +# ---------- environment ------------------------------------------------------ +# + +initialize: + cd environment/ && \ + $(_PYTHON_) initialize.py && \ + cd .. + +# +# ---------- conditional ------------------------------------------------------ +# + +ifeq ($(_SIGNATURE_),kaneton) + +# +# ---------- environment ------------------------------------------------------ +# + +clean: + $(call env_launch,$(_CLEAN_SCRIPT_),,) + +# +# ---------- variables -------------------------------------------------------- +# + +SUBDIRS := boot environment kaneton \ + license sample test tool \ + $(PATHS) + +# +# ---------- clear ------------------------------------------------------------ +# + +clear: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,clear,) ; \ + done + + $(call env_purge,) + +# +# ---------- prototypes ------------------------------------------------------- +# + +prototypes: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,prototypes,) ; \ + done + +# +# ---------- headers ---------------------------------------------------------- +# + +headers: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,headers,) ; \ + done + +# +# ---------- boot ------------------------------------------------------------- +# + +build: + $(call env_launch,$(_MBL_SCRIPT_),build,) + +install: main + $(call env_launch,$(_MBL_SCRIPT_),install,) + +# +# ---------- information ------------------------------------------------------ +# + +info: + $(call env_print,,,) + + $(call env_print,"--- ",blue,$(ENV_OPTION_NO_NEWLINE)) + $(call env_print,http://kaneton.opaak.org,,) + + $(call env_print,,,) + +endif diff --git a/boot/Makefile b/boot/Makefile new file mode 100644 index 0000000..7f13389 --- /dev/null +++ b/boot/Makefile @@ -0,0 +1,56 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/boot/Makefile +# +# created julien quintard [sun jun 10 14:54:43 2007] +# updated julien quintard [fri may 1 23:55:46 2009] +# + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers + +# +# ---------- variables -------------------------------------------------------- +# + +SUBDIRS := loader + +# +# ---------- rules ------------------------------------------------------------ +# + +main: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,,) ; \ + done + +clear: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,clear,) ; \ + done + + $(call env_purge,) + +prototypes: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,prototypes,) ; \ + done + +headers: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,headers,) ; \ + done diff --git a/boot/loader/Makefile b/boot/loader/Makefile new file mode 100644 index 0000000..346fe16 --- /dev/null +++ b/boot/loader/Makefile @@ -0,0 +1,47 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/boot/loader/Makefile +# +# created julien quintard [sun jun 10 14:54:43 2007] +# updated julien quintard [sat dec 18 11:15:05 2010] +# + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers + +# +# ---------- variables -------------------------------------------------------- +# + +SUBDIRS := ${_LOADER_DIR_} + +# +# ---------- rules ------------------------------------------------------------ +# + +main: + +clear: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,clear,) ; \ + done + + $(call env_purge,) + +prototypes: + +headers: diff --git a/boot/loader/ibm-pc.ia32/educational/Makefile b/boot/loader/ibm-pc.ia32/educational/Makefile new file mode 100644 index 0000000..2af0147 --- /dev/null +++ b/boot/loader/ibm-pc.ia32/educational/Makefile @@ -0,0 +1,50 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kane.../loader/ibm-pc.ia32/educational/Makefile +# +# created julien quintard [tue jun 12 20:34:41 2007] +# updated julien quintard [sat feb 5 11:09:53 2011] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := loader + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: + +prototypes: + +headers: + +dependencies: + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/boot/loader/ibm-pc.ia32/educational/loader b/boot/loader/ibm-pc.ia32/educational/loader new file mode 100755 index 0000000000000000000000000000000000000000..32cbb9ea1fac404a182f8d7e52b594e7c47e07b9 GIT binary patch literal 20441 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 Dh5DHm literal 0 HcmV?d00001 diff --git a/environment/Makefile b/environment/Makefile new file mode 100644 index 0000000..0f1c1d3 --- /dev/null +++ b/environment/Makefile @@ -0,0 +1,41 @@ +## +## licence kaneton licence +## +## project kaneton +## +## file /home/buckman/kaneton/environment/Makefile +## +## created julien quintard [fri feb 11 02:59:20 2005] +## updated matthieu bucchianeri [tue jan 24 11:42:31 2006] +## + +# +# ---------- dependencies ----------------------------------------------------- +# + +include env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main initialize clean clear prototypes headers + +# +# ---------- rules ------------------------------------------------------------ +# + +main: initialize + +initialize: + $(call env_launch,$(_ENVIRONMENT_DIR_)/initialize.py,,) + +clean: + $(call env_launch,$(_ENVIRONMENT_DIR_)/clean.py,,) + +clear: + $(call env_purge,) + +prototypes: + +headers: diff --git a/environment/__pycache__/critical.cpython-32.pyc b/environment/__pycache__/critical.cpython-32.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cc12339135a6423cc130a02c469baf474a172dda GIT binary patch literal 7612 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 literal 0 HcmV?d00001 diff --git a/environment/clean.py b/environment/clean.py new file mode 100644 index 0000000..283b2d7 --- /dev/null +++ b/environment/clean.py @@ -0,0 +1,138 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/environment/clean.py +# +# created julien quintard [sat dec 16 20:57:38 2006] +# updated julien quintard [mon apr 20 03:33:17 2009] +# + +# +# ---------- imports ---------------------------------------------------------- +# + +import os + +import env + +# +# ---------- functions -------------------------------------------------------- +# + +# +# machine() +# +# this function removes the links to the machine dependent files +# and directories. +# +def machine(): + env.display(env.HEADER_OK, + "removing links to machine-dependent directories", + env.OPTION_NONE) + + env.remove(env._GLUE_CURRENT_, env.OPTION_NONE) + env.remove(env._ARCHITECTURE_CURRENT_, env.OPTION_NONE) + env.remove(env._PLATFORM_CURRENT_, env.OPTION_NONE) + + + +# +# clear() +# +# this function clears the kaneton development tree. +# +def clear(): + env.display(env.HEADER_OK, + "clearing the kaneton development tree", + env.OPTION_NONE) + env.launch(env._SOURCE_DIR_ + "/Makefile", "clear", env.OPTION_QUIET) + + + +# +# boot() +# +# this function removes everything that has been generated +# for booting the kernel like images and so forth. +# +def boot(): + if env.path(env._IMAGE_, env.OPTION_EXIST): + env.remove(env._IMAGE_, env.OPTION_NONE) + + + +# +# dependencies() +# +# this function removes the kaneton dependencies. +# +def dependencies(): + dependencies = None + dep = None + + env.display(env.HEADER_OK, + "removing the kaneton header dependencies", + env.OPTION_NONE) + + dependencies = env.search(env._SOURCE_DIR_, + env._DEPENDENCY_MK_, + env.OPTION_FILE | env.OPTION_RECURSIVE) + + for dep in dependencies: + env.remove(dep, env.OPTION_NONE) + + + +# +# clean() +# +# the function removes the generated kaneton development environment files. +# +def clean(): + env.remove(env._ENV_MK_, env.OPTION_NONE) + env.remove(env._ENV_PY_, env.OPTION_NONE) + + + +# +# main() +# +# this function initializes the development environment. +# +import sys +def main(): + # display some stuff. + env.display(env.HEADER_OK, + "cleaning the kaneton development environment", + env.OPTION_NONE) + + # clear the kaneton development tree. + clear() + + # remove the generated boot stuff. + boot() + + # uninstall the chosen machine. + machine() + + # generate the kaneton dependencies. + dependencies() + + # remove the environment-specific files. + clean() + + # display some stuff. + env.display(env.HEADER_OK, + "environment development cleaned successfully", + env.OPTION_NONE) + +# +# ---------- entry point ------------------------------------------------------ +# + +if __name__ == "__main__": + main() diff --git a/environment/critical.py b/environment/critical.py new file mode 100644 index 0000000..bb8fa1e --- /dev/null +++ b/environment/critical.py @@ -0,0 +1,426 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/environment/critical.py +# +# created julien quintard [fri dec 15 13:43:03 2006] +# updated julien quintard [fri dec 17 20:00:58 2010] +# + +# +# ---------- imports ---------------------------------------------------------- +# + +import os +import sys +import re + +# +# ---------- globals ---------------------------------------------------------- +# + +g_directories = None +g_contents = None +g_assignments = [] + +g_variables = {} + +# +# ---------- functions -------------------------------------------------------- +# + +# +# error() +# +# this function displays an error and quit. +# +def error(message): + sys.stderr.write("[!] " + message) + sys.exit(42) + + + +# +# warning() +# +# this function simply displays an error. +# +def warning(msg): + sys.stderr.write("[!] " + msg) + + + +# +# load() +# +# this function takes an arbitray number of directories where pattern +# files could be located and load them in a single python string. +# +def load(directories, pattern): + content = "" + + directory = None + includes = None + include = None + handle = None + files = None + line = None + file = None + cwd = None + + for directory in directories: + if not os.path.isdir(directory): + continue + + files = os.listdir(directory); + + for file in files: + if not os.path.isfile(directory + "/" + file): + continue + + if not re.match(pattern, file): + continue + + try: + handle = open(directory + "/" + file, "r") + except IOError: + error("unable to open the file " + directory + "/" + file + ".\n") + + for line in handle.readlines(): + content += line + + content += "\n" + + includes = re.findall("(" \ + "^" \ + "include" \ + "[ \t]*" \ + "(.*)" \ + "\n" \ + ")", content, re.MULTILINE); + + for include in includes: + content = content.replace(include[0], + load([os.path.dirname(directory + + "/" + + include[1])], + "^" + + os.path.basename(directory + + "/" + + include[1]) + + "$")) + + handle.close() + + return content + + + +# +# comments() +# +# this function removes the comments from the file +# +def comments(): + global g_contents + comments = None + c = None + + comments = re.findall("^#.*$", g_contents, re.MULTILINE); + + for c in comments: + g_contents = g_contents.replace(c, "", 1) + + + +# +# locate() +# +# this function tries to locate the variable of the given array and +# return the corresponding tuple. +# +def locate(array, variable): + var = None + + for var in array: + if variable == var[0]: + return var + + return None + + + +# +# extract() +# +# this function extracts the variables assignments from the environment +# configuration files previously loaded. +# +def extract(): + global g_assignments + assignments = None + assignment = None + already = None + new = None + + assignments = re.findall("^" \ + "[ \t]*" \ + "([a-zA-Z0-9_]+)" \ + "[ \t]*" \ + "(\+?=)" \ + "[ \t]*" \ + "((?:(?:\\\\\n)|[^\n])*)" \ + "\n", g_contents, re.MULTILINE); + + for assignment in assignments: + # look for a previous registered assignment. + already = locate(g_assignments, assignment[0]) + + if already: + # if it is an assignment, just override the previous declaration. + if assignment[1] == "=": + new = (assignment[0], assignment[2]) + # if it is a concatenation, override the previous one with a + # concatenation of the two values. + elif assignment[1] == "+=": + new = (assignment[0], already[1] + " " + assignment[2]) + else: + error("unknown assignment token '" + assignment[1] + "' for the " + "variable '" + assignment[0] + "'.\n") + + # remove and insert the new tuple. + g_assignments.remove(already) + g_assignments.append(new) + else: + if assignment[1] == "=": + # simple insert the tuple. + new = (assignment[0], assignment[2]) + + g_assignments.append(new) + elif assignment[1] == "+=": + error("appending to the undefined variable '" + assignment[0] + + "' is not allowed.\n") + else: + error("unknown assignment token '" + assignment[1] + "' for the " + "variable '" + assignment[0] + "'.\n") + + + +# +# expand() +# +# this function tries to expand the given variable. +# +def expand(name, stack): + variables = None + position = None + tuple = None + value = None + var = None + + # try to get the variable's value from the already expanded variables. + try: + value = g_variables[name] + return + except: + # check if the variable was declared somewhere. + tuple = locate(g_assignments, name) + if not tuple: + warning("the kaneton environment variable " + name + + " is not defined\n") + value = "" + else: + value = tuple[1] + + # check recursion assignments. + try: + position = stack.index(name) + except: + pass + + if position != None: + error("the kaneton environment variable " + name + + " recursively references itself.\n") + + # locate, expand and replace the kaneton variables. + variables = re.findall("\$\{([^}]+)\}", value) + if variables: + for var in variables: + expand(var, stack + [name]) + for var in variables: + value = value.replace("${" + var + "}", g_variables[var]) + + # locate, expand and replace the shell variables. + variables = re.findall("\$\(([^}]+)\)", value) + if variables: + for var in variables: + if os.getenv(var) == None: + warning("shell user variable " + var + " is not defined\n") + value = value.replace("$(" + var + ")", "") + else: + value = value.replace("$(" + var + ")", os.getenv(var)) + + # finally register the new variable with its expanded value. + g_variables[name] = value + + + +# +# resolve() +# +# this function resolves the variable assignments. +def resolve(): + for assignment in g_assignments: + expand(assignment[0], []) + + + +# +# generate() +# +# this function generates the kaneton development environment files. +# +def generate(mfile, mcontent, pfile, pcontent): + mhandle = None + phandle = None + line = None + var = None + + # open the make environment file. + try: + mhandle = open(mfile, "w") + except IOError: + error("unable to open the file " + mfile + ".\n") + + # open the python environment file. + try: + phandle = open(pfile, "w") + except IOError: + error("unable to open the file " + pfile + ".\n") + + # inject the kaneton environment configuration for the make and python + # environment. + for var in g_variables: + mhandle.write(var + " := " + g_variables[var] + "\n") + phandle.write(var + " = " + "\"" + g_variables[var] + "\"" + "\n") + + # append the make environment files to the make environment file. + mhandle.write(mcontent) + + # append the python environment files to the python environment file. + phandle.write(pcontent) + + # close the files. + phandle.close() + mhandle.close() + + + +# +# main() +# +# this function builds a kaneton development environment. +# +def main(): + global g_directories + global g_contents + architecture = None + source_dir = None + platform = None + contents = None + machine = None + python = None + host = None + user = None + + # get the shell environment variables. + user = os.getenv("KANETON_USER") + host = os.getenv("KANETON_HOST") + python = os.getenv("KANETON_PYTHON") + platform = os.getenv("KANETON_PLATFORM") + architecture = os.getenv("KANETON_ARCHITECTURE") + + # check the presence of the shell environment variable. + if not (user != None and + os.path.isdir("profile/user/" + user)): + error("the shell environment variable KANETON_USER is not set " + \ + "or is incorrect.\n") + + if not (python != None and + os.path.isfile(python)): + error("the shell environment variable KANETON_PYTHON is not set " + \ + "or is incorrect.\n") + + if not (platform != None and + os.path.isdir("profile/kaneton/machine/platform/" + platform)): + error("the shell environment variable KANETON_PLATFORM is not " + \ + "set or is incorrect.\n") + + if not (architecture != None and + os.path.isdir("profile/kaneton/machine/architecture/" + architecture)): + error("the shell environment variable KANETON_ARCHITECTURE is " + \ + "not set or is incorrect.\n") + + if not (host != None and + os.path.isdir("profile/host/" + host + "." + architecture)): + error("the shell environment variable KANETON_HOST or " + \ + "KANETON_ARCHITECTURE is not set or is incorrect.\n") + + # set the configuration directories based on the user variables. + g_directories = ("profile/", + "profile/host/", + "profile/host/" + host + "." + architecture + "/", + "profile/boot/", + "profile/boot/" + platform + "." + architecture + "/", + "profile/kaneton/", + "profile/kaneton/core/", + "profile/kaneton/machine/", + "profile/kaneton/machine/platform/", + "profile/kaneton/machine/platform/" + platform + "/", + "profile/kaneton/machine/architecture/", + "profile/kaneton/machine/architecture/" + \ + architecture + "/", + "profile/kaneton/machine/glue/", + "profile/kaneton/machine/glue/" + platform + "." + \ + architecture + "/", + "profile/kaneton/library/", + "profile/kaneton/modules/", + "profile/user/", + "profile/user/" + user + "/") + + # first, set a virtual kaneton variable containing the location of + # the kaneton source directory. + # this directory is expected to be relatively located at: ../ + # since this script should have been launched in the environment/ + # directory + cwd = os.getcwd() + os.chdir("..") + source_dir = os.getcwd() + os.chdir(cwd) + + # load the content of the configuration files *.conf. + g_contents = "_SOURCE_DIR_ = " + source_dir + "\n" + g_contents += load(g_directories, "^.*\.conf$") + + # removes the comments + comments() + + # extract the assignments from the files. + extract() + + # resolve the variables in assignments. + resolve() + + # generate the development environment files. + generate("env.mk", load(g_directories, "^.*\.mk$"), + "env.py", load(g_directories, "^.*\.py$")) + +# +# ---------- entry point ------------------------------------------------------ +# + +main() diff --git a/environment/critical.pyc b/environment/critical.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2728cd3c370e79a57bce73b3c4ff36ed148ff5d2 GIT binary patch literal 7117 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 literal 0 HcmV?d00001 diff --git a/environment/initialize.py b/environment/initialize.py new file mode 100644 index 0000000..20bda31 --- /dev/null +++ b/environment/initialize.py @@ -0,0 +1,168 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/environment/initialize.py +# +# created julien quintard [fri dec 15 13:43:03 2006] +# updated julien quintard [thu feb 10 19:24:31 2011] +# + +# +# ---------- imports ---------------------------------------------------------- +# + +import critical + +import env + +import sys +import time + +# +# ---------- functions -------------------------------------------------------- +# + +# +# warning() +# +# this function displays the current configuration. +# +def warning(): + env.display(env.HEADER_NONE, "", env.OPTION_NONE) + env.display(env.HEADER_OK, "configuration:", env.OPTION_NONE) + env.display(env.HEADER_OK, + " user: " + env._USER_, + env.OPTION_NONE) + env.display(env.HEADER_OK, + " host: " + env._HOST_, + env.OPTION_NONE) + env.display(env.HEADER_OK, + " platform: " + env._PLATFORM_, + env.OPTION_NONE) + env.display(env.HEADER_OK, + " architecture: " + env._ARCHITECTURE_, + env.OPTION_NONE) + env.display(env.HEADER_NONE, "", env.OPTION_NONE) + + + +# +# machine() +# +# this function installs the links to the glue, platform and architecture +# dependent files and directories. +def machine(): + env.display(env.HEADER_OK, + "installing links to machine-dependent directories", + env.OPTION_NONE) + + env.remove(env._GLUE_CURRENT_, env.OPTION_NONE) + env.link(env._GLUE_CURRENT_, env._GLUE_DIR_, env.OPTION_NONE) + + env.remove(env._PLATFORM_CURRENT_, env.OPTION_NONE) + env.link(env._PLATFORM_CURRENT_, env._PLATFORM_DIR_, env.OPTION_NONE) + + env.remove(env._ARCHITECTURE_CURRENT_, env.OPTION_NONE) + env.link(env._ARCHITECTURE_CURRENT_, env._ARCHITECTURE_DIR_, env.OPTION_NONE) + + + +# +# check() +# +# this function looks for every binary the behaviour profile needs to work +# properly. +# +def check(): + binaries = None + binary = None + + env.display(env.HEADER_OK, + "looking for programs needed by the development environment", + env.OPTION_NONE) + + binaries = env._BINARIES_.split(",") + + for binary in binaries: + binary = binary.replace('\t', ' ') + binary = binary.strip() + + binary = binary.split(' ') + + if env.locate(binary[0], env.OPTION_NONE) != 0: + env.display(env.HEADER_ERROR, + " the program " + binary[0] + " is not present " \ + "on your system", + env.OPTION_NONE) + + + +# +# prototypes() +# +# this function generates the kaneton prototypes. +# +def prototypes(): + useless = None + + env.display(env.HEADER_OK, + "generating the kaneton prototypes", + env.OPTION_NONE) + env.launch(env._SOURCE_DIR_ + "/Makefile", "prototypes", env.OPTION_QUIET) + + + +# +# headers() +# +# this function generates the kaneton header dependencies. +# +def headers(): + env.display(env.HEADER_OK, + "generating the kaneton header dependencies", + env.OPTION_NONE) + env.launch(env._SOURCE_DIR_ + "/Makefile", "headers", env.OPTION_QUIET) + + + +# +# main() +# +# this function initializes the development environment. +# +def main(): + # display some stuff. + env.display(env.HEADER_OK, + "installing the kaneton development environment", + env.OPTION_NONE) + + # display the current configuration and ask the user to continue. + warning() + + # install the chosen machine: platform/architecture. + machine() + + # check the presence of the binaries used by the behaviour profile. + check() + + # generate the kaneton prototypes. + prototypes() + + # generate the kaneton headers. + headers() + + # display some stuff. + env.display(env.HEADER_OK, + "environment development installed successfully", + env.OPTION_NONE) + +# +# ---------- entry point ------------------------------------------------------ +# + +if __name__ == "__main__": + main() diff --git a/environment/profile/boot/boot.conf b/environment/profile/boot/boot.conf new file mode 100644 index 0000000..fdadc8f --- /dev/null +++ b/environment/profile/boot/boot.conf @@ -0,0 +1,76 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/environment/profile/boot/boot.conf +# +# created julien quintard [tue may 8 11:00:40 2007] +# updated julien quintard [tue may 5 10:16:49 2009] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file declares the default boot variables which should be overriden +# by the user profile. +# + +# +# ---------- multi-bootloader ------------------------------------------------- +# + +_MBL_SCRIPT_ = + +_BOOT_MODE_ = +_BOOT_DEVICE_ = + +# +# ---------- network ---------------------------------------------------------- +# + +_ADDRESS_ = + +# +# ---------- tftp server ------------------------------------------------------ +# + +_TFTP_ADDRESS_ = + +_TFTP_DIRECTORY_ = + +# +# ---------- devices ---------------------------------------------------------- +# + +_UDEVICE_ = + +_MDEVICE_ = + +_IMAGE_ = ${_PROFILE_USER_DIR_}/${_USER_}.img + +# +# ---------- inputs ---------------------------------------------------------- +# +# the inputs are additional files such as boot-time servers, configuration +# files. +# + +_INPUTS_ = + +# +# ---------- components ------------------------------------------------------- +# +# this variable contains the list of the components to build. +# + +_COMPONENT_FIRMWARE_ = +_COMPONENT_LOADER_ = ${_LOADER_} +_COMPONENT_KANETON_ = ${_KANETON_} + +_COMPONENTS_ = ${_COMPONENT_FIRMWARE_} \ + ${_COMPONENT_LOADER_} \ + ${_COMPONENT_KANETON_} \ + ${_INPUTS_} diff --git a/environment/profile/boot/boot.desc b/environment/profile/boot/boot.desc new file mode 100644 index 0000000..9211384 --- /dev/null +++ b/environment/profile/boot/boot.desc @@ -0,0 +1,23 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/environment/profile/user/boot.desc +# +# created julien quintard [tue may 8 13:00:28 2007] +# updated julien quintard [tue jun 26 15:20:39 2007] +# + +# +# ---------- information ------------------------------------------------------ +# +# XXX +# + +# XXX +# +# _BOOT_MODE_ = { peripheral, network, image } +# _BOOT_DEVICE = { floppy, hard-drive } diff --git a/environment/profile/environment.conf b/environment/profile/environment.conf new file mode 100644 index 0000000..30589b6 --- /dev/null +++ b/environment/profile/environment.conf @@ -0,0 +1,348 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/environment/profile/environment.conf +# +# created julien quintard [fri dec 15 13:50:15 2006] +# updated julien quintard [wed feb 9 05:33:19 2011] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file contains the default environment configuration including +# kaneton directories, librarires, script, tools etc. locations. +# + +# +# ---------- signature -------------------------------------------------------- +# + +_SIGNATURE_ = kaneton + +# +# ---------- shell environment variables -------------------------------------- +# + +_USER_ = $(KANETON_USER) +_HOST_ = $(KANETON_HOST) +_PYTHON_ = $(KANETON_PYTHON) +_PLATFORM_ = $(KANETON_PLATFORM) +_ARCHITECTURE_ = $(KANETON_ARCHITECTURE) + +# +# ---------- source directory ------------------------------------------------- +# + +_BOOT_DIR_ = ${_SOURCE_DIR_}/boot +_CHEAT_DIR_ = ${_SOURCE_DIR_}/cheat +_CONFIGURE_DIR_ = ${_SOURCE_DIR_}/configure +_ENVIRONMENT_DIR_ = ${_SOURCE_DIR_}/environment +_EXPORT_DIR_ = ${_SOURCE_DIR_}/export +_HISTORY_DIR_ = ${_SOURCE_DIR_}/history +_KANETON_DIR_ = ${_SOURCE_DIR_}/kaneton +_LICENSE_DIR_ = ${_SOURCE_DIR_}/license +_SAMPLE_DIR_ = ${_SOURCE_DIR_}/sample +_TEST_DIR_ = ${_SOURCE_DIR_}/test +_TOOL_DIR_ = ${_SOURCE_DIR_}/tool +_TRANSCRIPT_DIR_ = ${_SOURCE_DIR_}/transcript +_VIEW_DIR_ = ${_SOURCE_DIR_}/view + +# +# ---------- boot directory --------------------------------------------------- +# + +_BOOT_STRAP_DIR_ = ${_BOOT_DIR_}/strap +_BOOT_LOADER_DIR_ = ${_BOOT_DIR_}/loader + +_LOADER_DIR_ = ${_BOOT_LOADER_DIR_}/${_PLATFORM_}.${_ARCHITECTURE_} + +# +# ---------- kaneton directory ------------------------------------------------ +# + +_CORE_DIR_ = ${_KANETON_DIR_}/core +_MACHINE_DIR_ = ${_KANETON_DIR_}/machine +_LIBRARY_DIR_ = ${_KANETON_DIR_}/library +_MODULES_DIR_ = ${_KANETON_DIR_}/modules +_INCLUDE_DIR_ = ${_KANETON_DIR_}/include + +# +# ---------- core directory --------------------------------------------------- +# + +_CORE_AS_DIR_ = ${_CORE_DIR_}/as +_CORE_CAPABILITY_DIR_ = ${_CORE_DIR_}/capability +_CORE_CLOCK_DIR_ = ${_CORE_DIR_}/clock +_CORE_CPU_DIR_ = ${_CORE_DIR_}/cpu +_CORE_EVENT_DIR_ = ${_CORE_DIR_}/event +_CORE_ID_DIR_ = ${_CORE_DIR_}/id +_CORE_INCLUDE_DIR_ = ${_CORE_DIR_}/include +_CORE_INTERFACE_DIR_ = ${_CORE_DIR_}/interface +_CORE_IO_DIR_ = ${_CORE_DIR_}/io +_CORE_KERNEL_DIR_ = ${_CORE_DIR_}/kernel +_CORE_MAP_DIR_ = ${_CORE_DIR_}/map +_CORE_MESSAGE_DIR_ = ${_CORE_DIR_}/message +_CORE_REGION_DIR_ = ${_CORE_DIR_}/region +_CORE_SCHEDULER_DIR_ = ${_CORE_DIR_}/scheduler +_CORE_SEGMENT_DIR_ = ${_CORE_DIR_}/segment +_CORE_SET_DIR_ = ${_CORE_DIR_}/set +_CORE_TASK_DIR_ = ${_CORE_DIR_}/task +_CORE_THREAD_DIR_ = ${_CORE_DIR_}/thread +_CORE_TIMER_DIR_ = ${_CORE_DIR_}/timer + +# +# ---------- machine directory ------------------------------------------------ +# + +_MACHINE_GLUE_DIR_ = ${_MACHINE_DIR_}/glue +_MACHINE_ARCHITECTURE_DIR_ = ${_MACHINE_DIR_}/architecture +_MACHINE_PLATFORM_DIR_ = ${_MACHINE_DIR_}/platform +_MACHINE_INCLUDE_DIR_ = ${_MACHINE_DIR_}/include + +# +# ---------- library directory ------------------------------------------------ +# + +_LIBRARY_INCLUDE_DIR_ = ${_LIBRARY_DIR_}/include + +# +# ---------- modules directory ------------------------------------------------ +# + +_MODULES_INCLUDE_DIR_ = ${_MODULES_DIR_}/modules + +# +# ---------- glue directory --------------------------------------------------- +# + +_GLUE_DIR_ = ${_MACHINE_GLUE_DIR_}/${_PLATFORM_}.${_ARCHITECTURE_} + +_GLUE_INCLUDE_DIR_ = ${_GLUE_DIR_}/include + +# +# ---------- platform directory ----------------------------------------------- +# + +_PLATFORM_DIR_ = ${_MACHINE_PLATFORM_DIR_}/${_PLATFORM_} + +_PLATFORM_INCLUDE_DIR_ = ${_PLATFORM_DIR_}/include + +# +# ---------- architecture directory ------------------------------------------- +# + +_ARCHITECTURE_DIR_ = ${_MACHINE_ARCHITECTURE_DIR_}/${_ARCHITECTURE_} + +_ARCHITECTURE_INCLUDE_DIR_ = ${_ARCHITECTURE_DIR_}/include + +# +# ---------- kaneton library objects ------------------------------------------ +# + +_CORE_LO_ = ${_CORE_DIR_}/core.lo +_MACHINE_LO_ = ${_MACHINE_DIR_}/machine.lo +_LIBRARY_LO_ = ${_LIBRARY_DIR_}/library.lo +_MODULES_LO_ = ${_MODULES_DIR_}/modules.lo + +_GLUE_LO_ = ${_GLUE_DIR_}/glue.lo +_ARCHITECTURE_LO_ = ${_ARCHITECTURE_DIR_}/architecture.lo +_PLATFORM_LO_ = ${_PLATFORM_DIR_}/platform.lo + +# +# ---------- current selected machine ----------------------------------------- +# + +_GLUE_CURRENT_ = ${_MACHINE_GLUE_DIR_}/.current +_ARCHITECTURE_CURRENT_ = ${_MACHINE_ARCHITECTURE_DIR_}/.current +_PLATFORM_CURRENT_ = ${_MACHINE_PLATFORM_DIR_}/.current + +# +# ---------- environment directory -------------------------------------------- +# + +_PROFILE_DIR_ = ${_ENVIRONMENT_DIR_}/profile + +# +# ---------- profile directory ------------------------------------------------ +# + +_PROFILE_BOOT_DIR_ = ${_PROFILE_DIR_}/boot +_PROFILE_HOST_DIR_ = ${_PROFILE_DIR_}/host/${_HOST_}.${_ARCHITECTURE_} +_PROFILE_KANETON_DIR_ = ${_PROFILE_DIR_}/kaneton +_PROFILE_USER_DIR_ = ${_PROFILE_DIR_}/user/${_USER_} + +_PROFILE_CORE_DIR_ = ${_PROFILE_KANETON_DIR_}/core +_PROFILE_LIBRARY_DIR_ = ${_PROFILE_KANETON_DIR_}/library +_PROFILE_MACHINE_DIR_ = ${_PROFILE_KANETON_DIR_}/machine +_PROFILE_MODULES_DIR_ = ${_PROFILE_KANETON_DIR_}/modules + +_PROFILE_GLUE_DIR_ = ${_PROFILE_MACHINE_DIR_}/glue/${_PLATFORM_}.${_ARCHITECTURE_} +_PROFILE_PLATFORM_DIR_ = ${_PROFILE_MACHINE_DIR_}/platform/${_PLATFORM_} +_PROFILE_ARCHITECTURE_DIR_ = ${_PROFILE_MACHINE_DIR_}/architecture/${_ARCHITECTURE_} + +# +# ---------- view directory --------------------------------------------------- +# + +_BIBLIOGRAPHY_DIR_ = ${_VIEW_DIR_}/bibliography +_BOOK_DIR_ = ${_VIEW_DIR_}/book +_EXAM_DIR_ = ${_VIEW_DIR_}/exam +_FEEDBACK_DIR_ = ${_VIEW_DIR_}/feedback +_FIGURES_DIR_ = ${_VIEW_DIR_}/figures +_INTERNSHIP_DIR_ = ${_VIEW_DIR_}/internship +_LECTURE_DIR_ = ${_VIEW_DIR_}/lecture +_LOGO_DIR_ = ${_VIEW_DIR_}/logo +_PACKAGE_DIR_ = ${_VIEW_DIR_}/package +_PAPER_DIR_ = ${_VIEW_DIR_}/paper +_TALK_DIR_ = ${_VIEW_DIR_}/talk +_TEMPLATE_DIR_ = ${_VIEW_DIR_}/template + +# +# ---------- layouts ---------------------------------------------------------- +# + +_LAYOUT_DIR_ = ${_GLUE_DIR_}/layout + +_KERNEL_LAYOUT_ = ${_LAYOUT_DIR_}/kernel.lyt +_DRIVER_LAYOUT_ = ${_LAYOUT_DIR_}/driver.lyt +_SERVICE_LAYOUT_ = ${_LAYOUT_DIR_}/service.lyt +_GUEST_LAYOUT_ = ${_LAYOUT_DIR_}/guest.lyt + +# +# ---------- tool directory --------------------------------------------------- +# + +_CTC_DIR_ = ${_TOOL_DIR_}/ctc +_FIRMWARE_DIR_ = ${_TOOL_DIR_}/firmware/${_PLATFORM_}.${_ARCHITECTURE_} +_MBL_DIR_ = ${_TOOL_DIR_}/mbl +_MKP_DIR_ = ${_TOOL_DIR_}/mkp +_SCRIPT_DIR_ = ${_TOOL_DIR_}/script + +# +# ---------- core library-objects --------------------------------------------- +# + +_AS_LO_ = ${_CORE_AS_DIR_}/as.lo +_CAPABILITY_LO_ = ${_CORE_CAPABILITY_DIR_}/capability.lo +_CLOCK_LO_ = ${_CORE_CLOCK_DIR_}/clock.lo +_CPU_LO_ = ${_CORE_CPU_DIR_}/cpu.lo +_EVENT_LO_ = ${_CORE_EVENT_DIR_}/event.lo +_ID_LO_ = ${_CORE_ID_DIR_}/id.lo +_INTERFACE_LO_ = ${_CORE_INTERFACE_DIR_}/interface.lo +_IO_LO_ = ${_CORE_IO_DIR_}/io.lo +_KERNEL_LO_ = ${_CORE_KERNEL_DIR_}/kernel.lo +_MAP_LO_ = ${_CORE_MAP_DIR_}/map.lo +_MESSAGE_LO_ = ${_CORE_MESSAGE_DIR_}/message.lo +_REGION_LO_ = ${_CORE_REGION_DIR_}/region.lo +_SCHEDULER_LO_ = ${_CORE_SCHEDULER_DIR_}/scheduler.lo +_SEGMENT_LO_ = ${_CORE_SEGMENT_DIR_}/segment.lo +_SET_LO_ = ${_CORE_SET_DIR_}/set.lo +_TASK_LO_ = ${_CORE_TASK_DIR_}/task.lo +_THREAD_LO_ = ${_CORE_THREAD_DIR_}/thread.lo +_TIMER_LO_ = ${_CORE_TIMER_DIR_}/timer.lo + +# +# ---------- binaries --------------------------------------------------------- +# + +_LOADER_ = ${_LOADER_DIR_}/loader +_KANETON_ = ${_KANETON_DIR_}/kaneton + +# +# ---------- environment files ------------------------------------------------ +# + +_ENV_MK_ = ${_ENVIRONMENT_DIR_}/env.mk +_ENV_PY_ = ${_ENVIRONMENT_DIR_}/env.py + +# +# ---------- tools ------------------------------------------------------------ +# + +_CTC_BUILD_TOOL_ = ${_CTC_DIR_}/buildctf +_CTC_GATHER_TOOL_ = ${_CTC_DIR_}/enhashctf +_CTC_COMPARE_TOOL_ = ${_CTC_DIR_}/ctcompare +_CTC_SHOW_TOOL_ = ${_CTC_DIR_}/showkeys +_CTC_DETOK_TOOL_ = ${_CTC_DIR_}/detok + +_MKP_TOOL_ = ${_MKP_DIR_}/mkp.py +_REPLAY_TOOL_ = ${_SCRIPT_DIR_}/scriptreplay.pl + +# +# ---------- test ------------------------------------------------------------- +# + +_TEST_CLIENT_DIR_ = ${_TEST_DIR_}/client +_TEST_CONFIGURATION_DIR_ = ${_TEST_DIR_}/configuration +_TEST_ENGINE_DIR_ = ${_TEST_DIR_}/engine +_TEST_PACKAGES_DIR_ = ${_TEST_DIR_}/packages +_TEST_SERVER_DIR_ = ${_TEST_DIR_}/server +_TEST_STORE_DIR_ = ${_TEST_DIR_}/store +_TEST_SUITES_DIR_ = ${_TEST_DIR_}/suites +_TEST_TESTS_DIR_ = ${_TEST_DIR_}/tests +_TEST_UTILITIES_DIR_ = ${_TEST_DIR_}/utilities +_TEST_SUITES_DIR_ = ${_TEST_DIR_}/suites +_TEST_HOOKS_DIR_ = ${_TEST_DIR_}/hooks + +_TEST_TESTS_BOOT_DIR_ = ${_TEST_TESTS_DIR_}/boot +_TEST_TESTS_KANETON_DIR_ = ${_TEST_TESTS_DIR_}/kaneton + +_TEST_ENGINE_LO_ = ${_TEST_ENGINE_DIR_}/engine.lo + +_TEST_STORE_CAPABILITY_DIR_ = ${_TEST_STORE_DIR_}/capability +_TEST_STORE_CERTIFICATE_DIR_ = ${_TEST_STORE_DIR_}/certificate +_TEST_STORE_CODE_DIR_ = ${_TEST_STORE_DIR_}/code +_TEST_STORE_DATABASE_DIR_ = ${_TEST_STORE_DIR_}/database +_TEST_STORE_KEY_DIR_ = ${_TEST_STORE_DIR_}/key +_TEST_STORE_REPORT_DIR_ = ${_TEST_STORE_DIR_}/report +_TEST_STORE_BUNDLE_DIR_ = ${_TEST_STORE_DIR_}/bundle/${_PLATFORM_}.${_ARCHITECTURE_} +_TEST_STORE_SNAPSHOT_DIR_ = ${_TEST_STORE_DIR_}/snapshot + +_TEST_STORE_BUNDLE_BOOT_LO_ = ${_TEST_STORE_BUNDLE_DIR_}/boot.lo +_TEST_STORE_BUNDLE_KANETON_LO_ = ${_TEST_STORE_BUNDLE_DIR_}/kaneton.lo + +_TEST_CLIENT_SCRIPT_ = ${_TEST_CLIENT_DIR_}/client.py +_TEST_SERVER_SCRIPT_ = ${_TEST_SERVER_DIR_}/server.py + +_TEST_CAPABILITY_SCRIPT_ = ${_TEST_UTILITIES_DIR_}/capability.py +_TEST_CERTIFICATE_SCRIPT_ = ${_TEST_UTILITIES_DIR_}/certificate.py +_TEST_CODE_SCRIPT_ = ${_TEST_UTILITIES_DIR_}/code.py +_TEST_DATABASE_SCRIPT_ = ${_TEST_UTILITIES_DIR_}/database.py +_TEST_INVENTORY_SCRIPT_ = ${_TEST_UTILITIES_DIR_}/inventory.py + +# +# ---------- scripts ---------------------------------------------------------- +# + +_INITIALIZE_SCRIPT_ = ${_ENVIRONMENT_DIR_}/initialize.py +_CLEAN_SCRIPT_ = ${_ENVIRONMENT_DIR_}/clean.py + +_CONFIGURE_SCRIPT_ = ${_CONFIGURE_DIR_}/configure.py +_EXPORT_SCRIPT_ = ${_EXPORT_DIR_}/export.py +_CHEAT_SCRIPT_ = ${_CHEAT_DIR_}/cheat.py +_RECORD_SCRIPT_ = ${_TRANSCRIPT_DIR_}/record.py +_PLAY_SCRIPT_ = ${_TRANSCRIPT_DIR_}/play.py +_VIEW_SCRIPT_ = ${_VIEW_DIR_}/view.py + +# +# ---------- makefile dependency file ----------------------------------------- +# + +_DEPENDENCY_MK_ = .dependency.mk + +# +# ---------- latex dependency file -------------------------------------------- +# + +_DEPENDENCY_TEX_ = .dependency.tex + +# +# ---------- python package directories --------------------------------------- +# + +_PYTHON_INCLUDE_DIR_ = ${_ENVIRONMENT_DIR_}:${_TEST_PACKAGES_DIR_} diff --git a/environment/profile/host/darwin/darwin.conf b/environment/profile/host/darwin/darwin.conf new file mode 100644 index 0000000..18610b1 --- /dev/null +++ b/environment/profile/host/darwin/darwin.conf @@ -0,0 +1,113 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kane...ironment/profile/host/darwin/darwin.conf +# +# created julien quintard [tue may 8 13:19:52 2007] +# updated julien quintard [fri may 1 02:11:38 2009] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file contains the libraries, binaries, script and tools related to +# the generic darwin host sub-profile. +# + +# +# ---------- includes --------------------------------------------------------- +# + +_INCLUDES_ = -I${_INCLUDE_DIR_} + +# +# ---------- flags ------------------------------------------------------------ +# + +_CC_FLAGS_ = -D___kaneton \ + ${_INCLUDES_} \ + ${_KANETON_FLAGS_} \ + -fno-builtin \ + -Wimplicit \ + -Wparentheses \ + -Wreturn-type \ + -Wswitch -Wswitch-enum \ + -Wunused-function \ + -Wunused-variable \ + -Wmissing-prototypes \ + -Wmissing-declarations \ + -Wall + +_LD_FLAGS_ = ${_INCLUDES_} + +_AS_FLAGS_ = + +_CPP_FLAGS_ = + +_SHELL_FLAGS_ = + +_PYTHON_FLAGS_ = + +_PERL_FLAGS_ = + +_MAKE_FLAGS_ = + +# +# ---------- binaries --------------------------------------------------------- +# + +_BINARIES_ = ${_SHELL_}, ${_CC_}, ${_MAKE_}, \ + ${_RM_}, ${_AR_}, ${_RANLIB_}, \ + ${_LD_}, ${_AS_}, ${_BIBTEX_},\ + ${_LN_}, ${_TOUCH_}, ${_WC_}, \ + ${_DATE_}, ${_TAIL_}, ${_TAR_}, \ + ${_PDFLATEX_}, ${_CP_}, \ + ${_CAT_}, ${_SED_}, ${_CPP_}, \ + ${_MTOOLS_}, ${_MCOPY_}, \ + ${_XPDF_}, ${_MKTEMP_}, ${_MV_},\ + ${_LEX_}, ${_SCRIPT_}, \ + ${_PERL_}, ${_PYTHON_}, \ + ${_DIRNAME_}, ${_BASENAME_}, \ + ${_WHICH_} + +_SHELL_ = bash +_CC_ = i386-elf-gcc-3.4.6 +_AS_ = i386-elf-as +_MAKE_ = gmake +_RM_ = rm -f +_PURGE_ = ${_RM_} *.pyc *~ .*~ \#* .\#* +_AR_ = i386-elf-ar cq +_RANLIB_ = i386-elf-ranlib +_CD_ = cd +_LD_ = i386-elf-ld +_LN_ = ln -s -f +_TOUCH_ = touch +_WC_ = wc +_DATE_ = date -u +_TAIL_ = tail +_TAR_ = tar +_PDFLATEX_ = pdflatex +_BIBTEX_ = bibtex +_CP_ = cp +_CAT_ = cat +_SED_ = sed -r +_ECHO_ = /bin/echo +_CPP_ = i386-elf-cpp-3.4.6 +_MTOOLS_ = mtools +_MCOPY_ = mcopy +_XPDF_ = open +_MKTEMP_ = mktemp +_MV_ = mv +_PWD_ = pwd +_LEX_ = lex -t +_SCRIPT_ = script +_PERL_ = perl +_PYTHON_ = python2.5 +_DIRNAME_ = dirname +_BASENAME_ = basename +_WHICH_ = which +_EXIT_ = exit diff --git a/environment/profile/host/darwin/darwin.desc b/environment/profile/host/darwin/darwin.desc new file mode 100644 index 0000000..95aec91 --- /dev/null +++ b/environment/profile/host/darwin/darwin.desc @@ -0,0 +1,33 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/fanfwe/kane...nvironment/profile/host/darwin/darwin.desc +# +# created julien quintard [tue may 8 13:20:04 2007] +# updated francois goudal [mon aug 4 15:54:31 2008] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file describes the darwin environment variables. +# + +- variable: _SHELL_ + string: Shell + type: any + description: The shell binary path + +- variable: _CC_ + string: C Compiler + type: any + description: The C compiler binary path + +- variable: _MAKE_ + string: Make + type: any + description: The make binary path diff --git a/environment/profile/host/darwin/darwin.mk b/environment/profile/host/darwin/darwin.mk new file mode 100644 index 0000000..d39d662 --- /dev/null +++ b/environment/profile/host/darwin/darwin.mk @@ -0,0 +1,493 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kane...nvironment/profile/host/darwin/darwin.mk +# +# created julien quintard [tue may 8 13:03:34 2007] +# updated julien quintard [fri aug 29 20:03:17 2008] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file implements the remaining functions of the kaneton make interface. +# +# indeed the major generic part of the interface is already provided by the +# host profile. +# + +# +# ---------- python path ------------------------------------------------------ +# + +export PYTHONPATH=$(shell $(_ECHO_) $${PYTHONPATH}):$(_PYTHON_INCLUDE_DIR_) + +# +# ---------- functions -------------------------------------------------------- +# + +# +# colorize functions +# +# 1: text +# 2: options +# + +define env_colorize-black + "$(1)" +endef + +define env_colorize-red + "$(1)" +endef + +define env_colorize-green + "$(1)" +endef + +define env_colorize-yellow + "$(1)" +endef + +define env_colorize-blue + "$(1)" +endef + +define env_colorize-magenta + "$(1)" +endef + +define env_colorize-cyan + "$(1)" +endef + +define env_colorize-white + "$(1)" +endef + +define env_colorize- + "$(1)" +endef + +# +# perform function +# +# 1: command +# + +ifeq ($(_OUTPUT_),$(_OUTPUT_VERBOSE_)) # if the user wants to display + # additional debug information + +define env_perform + $(_ECHO_) " $(1)" && \ + $(1) +endef + +else + +define env_perform + $(1) +endef + +endif + +# +# print functions wrapper +# +# 1: text +# 2: color +# 3: options +# + +ifeq ($(_DISPLAY_),$(_DISPLAY_COLORED_)) # if the user wants to display + # the text with color + +define env_print + print_options="" && \ + if [ -n "$(3)" ] ; then \ + if [ $$(( $(3) & $(ENV_OPTION_NO_NEWLINE) )) -ne 0 ] ; then \ + print_options="$${print_options} -n" ; \ + fi ; \ + fi && \ + $(_ECHO_) $${print_options} $(call env_colorize-$(2),$(1),) +endef + +else # if not ... + +define env_print + print_options="" && \ + if [ -n "$(3)" ] ; then \ + if [ $$(( $(3) & $(ENV_OPTION_NO_NEWLINE) )) -ne 0 ] ; then \ + print_options="$${print_options} -n" ; \ + fi ; \ + fi && \ + $(_ECHO_) $${print_options} $(1) +endef + +endif + +# +# change the current working directory +# +# $(1): directory +# $(2): options +# + +define env_cd + cd_options="" && \ + $(call env_perform, \ + $(_CD_) $${cd_options} $(1)) +endef + +# +# returns a file contents +# +# $(1): the file +# $(2): options +# + +define env_pull + $(_CAT_) $(2) $(1) +endef + +# +# launch a new program/script/make etc. +# +# $(1): file +# $(2): arguments +# $(3): options +# + +define env_launch + launch_options="" && \ + cwd=$$($(_PWD_)) && \ + directory=$$($(_DIRNAME_) $(1)) && \ + file=$$($(_BASENAME_) $(1)) && \ + if [ "$${directory}" != "." ] ; then \ + $(call env_perform, \ + $(_CD_) $${directory}) ; \ + fi && \ + case "$${file}" in \ + *.sh) \ + $(call env_perform, \ + $(_SHELL_) $${launch_options} $${file} $(_SHELL_FLAGS_) $(2)) ; \ + ;; \ + *.py) \ + $(call env_perform, \ + $(_PYTHON_) $${launch_options} $${file} $(_PYTHON_FLAGS_) $(2)) ; \ + ;; \ + *.pl) \ + $(call env_perform, \ + $(_PERL_) $${launch_options} $${file} $(_PERL_FLAGS_) $(2)) ; \ + ;; \ + Makefile) \ + $(call env_perform, \ + $(_MAKE_) $${launch_options} -f $${file} $(_MAKE_FLAGS_) ${2}) ; \ + ;; \ + esac ; \ + return=$${?} && \ + if [ "$${directory}" != "." ] ; then \ + $(call env_perform, \ + $(_CD_) $${cwd}) ; \ + fi && \ + if [ $${return} -ne 0 ] ; then \ + $(_EXIT_) 42 ; \ + fi +endef + +# +# from c file to preprocessed c file +# +# $(1): preprocessed file +# $(2): c file +# $(3): options +# + +define env_preprocess + preprocess_options="" && \ + $(call env_display,green,PREPROCESS,$(2), ,) && \ + $(call env_perform, \ + $(_CPP_) -P $(_CPP_FLAGS_) $${preprocess_options} $(2) -o $(1)) +endef + +# +# from c file to object file +# +# $(1): object file +# $(2): c file +# $(3): options +# + +define env_compile-c + compile_c_options="" && \ + $(call env_display,green,COMPILE-C,$(2), ,) && \ + $(call env_perform, \ + $(_CC_) $(_CC_FLAGS_) $${compile_c_options} -c $(2) -o $(1)) +endef + +# +# from lex file to c file +# +# $(1): c file +# $(2): lex file +# $(3): options +# + +define env_lex-l + lex_l_options="" && \ + $(call env_display,green,LEX-L,$(2), ,) && \ + $(call env_perform, \ + $(_LEX_) $${lex_l_options} $(2) > $(1)) +endef + +# +# from S file to object file +# +# $(1): object file +# $(2): S file +# $(3): options +# + +define env_assemble-S + assemble_S_options="" && \ + $(call env_display,green,ASSEMBLE-S,$(2), ,) && \ + $(call env_perform, \ + $(_CPP_) $(2) | $(_AS_) $(_AS_FLAGS_) $${assemble_S_options} -o $(1)) +endef + +# +# create a static library from object files +# +# $(1): static library file name +# $(2): object files +# $(3): options +# + +define env_static-library + static_library_options="" && \ + $(call env_display,magenta,STATIC-LIBRARY,$(1), ,) && \ + $(call env_perform, \ + $(_AR_) $${static_library_options} $(1) $(2)) && \ + $(call env_perform, \ + $(_RANLIB_) $${static_library_options} $(1)) +endef + +# +# create a dynamic library from object files, static libraries +# and/or dynamic libraries +# +# $(1): dynamic library file name +# $(2): objects files and/or libraries +# $(3): options +# + +define env_dynamic-library + dynamic_library_options="" && \ + $(call env_display,magenta,DYNAMIC-LIBRARY,$(1), ,) && \ + $(call env_perform, \ + $(_LD_) --shared $(_LD_FLAGS_) $${dynamic_library_options} \ + -o $(1) $(2)) +endef + +# +# create an executable file from object file and/or library files +# +# $(1): executable file name +# $(2): objects files and/or libraries +# $(3): layout file +# $(4): options +# + +define env_executable + executable_options="" && \ + if [ -n "$(3)" ] ; then \ + executable_options="$${executable_options} -T $(3)" ; \ + fi && \ + if [ -n "$(4)" ] ; then \ + if [ $$(( $(4) & $(ENV_OPTION_NO_STANDARD) )) -ne 0 ] ; then \ + executable_options="$${executable_options} -nostdinc -nostdlib" ; \ + fi ; \ + fi && \ + $(call env_display,magenta,EXECUTABLE,$(1), ,) && \ + $(call env_perform, \ + $(_CC_) $(_CC_FLAGS_) $(_LD_FLAGS_) $${executable_options} -o $(1) \ + $(2) "`$(_CC_) -print-libgcc-file-name`") +endef + +# +# create an archive file from multiple object files +# +# note that the archive file is also an object file +# +# $(1): archive file name +# $(2): objects files and/or libraries +# $(3): options +# + +define env_archive + archive_options="" && \ + $(call env_display,magenta,ARCHIVE,$(1), ,) && \ + $(call env_perform, \ + $(_LD_) -r $(_LD_FLAGS_) $${archive_options} -o $(1) $(2)) +endef + +# +# remove the files +# +# $(1): files +# $(2): options +# + +define env_remove + remove_options="" && \ + for f in $(1) ; do \ + if [ -e $${f} ] ; then \ + $(call env_display,red,REMOVE,$${f}, ,) ; \ + fi && \ + $(call env_perform, \ + $(_RM_) $${remove_options} $${f}) ; \ + done +endef + +# +# purge i.e clean the directory from unwanted files +# + +define env_purge + $(call env_display,red,PURGE,,,) && \ + $(call env_perform, \ + $(_PURGE_)) +endef + +# +# generate prototypes from a source file +# +# $(1): file list +# $(2): options +# + +define env_prototypes + prototypes_options="" && \ + for f in $(1) ; do \ + if [ -e $${f} ] ; then \ + $(call env_display,yellow,PROTOTYPES,$${f}, ,) && \ + $(call env_launch,$(_MKP_TOOL_), \ + $${prototypes_options} $${f},) ; \ + fi ; \ + done +endef + +# +# genereate header dependencies +# +# $(1): the files for which the dependencies are generated +# $(2): options +# + +define env_headers + headers_options="" && \ + for f in $(1) ; do \ + if [ -e $${f} ] ; then \ + $(call env_display,yellow,HEADERS,$$f, ,) && \ + $(call env_perform, \ + $(_CC_) $(_CC_FLAGS_) -M -MG $${headers_options} \ + $${f} >> $(_DEPENDENCY_MK_)) ; \ + fi ; \ + done +endef + +# +# generate a version file +# +# $(1): the version file to generate +# + +define env_version + $(call env_display,yellow,VERSION,$(1), ,) && \ + $(_ECHO_) -n "" > $(1) && \ + $(_ECHO_) -n "const char version[] = \"$(_TITLE_)-$(_VERSION_)" >> $(1) && \ + $(_ECHO_) " "$(shell LANG=C $(_DATE_))" $(USER)@$(HOSTNAME)\";" >> $(1) +endef + +# +# create a link between two files +# +# $(1): link created +# $(2): destination +# $(3): options +# + +define env_link + link_options="" && \ + $(call env_display,cyan,LINK,$(1), ,) && \ + $(call env_perform, \ + $(_LN_) $${link_options} $(2) $(1)) +endef + +# +# compile a tex document +# +# $(1): the file name without extension +# $(2): options +# + +define env_compile-tex + compile_tex_options="" && \ + $(call env_display,green,COMPILE-TEX,$(1), ,) && \ + $(call env_perform, \ + $(_PDFLATEX_) $${compile_tex_options} $(1).tex) && \ + $(call env_perform, \ + $(_BIBTEX_) $(1).tex) && \ + $(call env_perform, \ + $(_PDFLATEX_) $${compile_tex_options} $(1).tex) ; \ + $(call env_perform, \ + $(_PDFLATEX_) $${compile_tex_options} $(1).tex) +endef + +# +# build a document +# +# $(1): the file name without extension +# $(2): options +# + +define env_document + if [ -n "$(2)" ] ; then \ + if [ $$(( $(2) & $(ENV_OPTION_PRIVATE) )) -ne 0 ] ; then \ + $(_ECHO_) '\def\mode{private}' > $(_DEPENDENCY_TEX_) ; \ + fi ; \ + fi && \ + if [ ! -f $(_DEPENDENCY_TEX_) ] ; then \ + $(_ECHO_) '\def\mode{public}' > $(_DEPENDENCY_TEX_) ; \ + fi && \ + $(call env_compile-tex,$(1),$(2)) +endef + +# +# launch a document viewer +# +# $(1): the file name without extension +# $(2): options +# + +define env_view + $(call env_display,yellow,VIEW,$(1), ,) && \ + $(call env_perform, \ + $(_XPDF_) $(1).pdf) +endef + +# +# ---------- component-based behaviour ---------------------------------------- +# + +# +# meta definitions +# + +ifneq ($(call findstring,kaneton,$(components)),) + _CC_FLAGS_ += -D___kaneton$$\kernel +endif diff --git a/environment/profile/host/darwin/darwin.py b/environment/profile/host/darwin/darwin.py new file mode 100644 index 0000000..d2e2137 --- /dev/null +++ b/environment/profile/host/darwin/darwin.py @@ -0,0 +1,226 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kane...nvironment/profile/host/darwin/darwin.py +# +# created julien quintard [tue may 8 13:20:21 2007] +# updated julien quintard [tue may 5 12:10:33 2009] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file implements the remaining functions of the kaneton python interface. +# +# note that the host profile already provides many functions. these +# functions can be overriden but you will probably just use them. +# +# in addition, the host profile already imports some packages. +# + +# +# ---------- python path ------------------------------------------------------ +# + +pythonpath = os.getenv("PYTHONPATH") +if not pythonpath: + pythonpath = "" + +os.putenv("PYTHONPATH", pythonpath + ":" + _PYTHON_INCLUDE_DIR_) + +# +# ---------- functions -------------------------------------------------------- +# + +# +# colorize() +# +# this function returns a colorized text if the environment is configured +# to or simply the original text. +# +# note that this function implementation is based on UNIX escape sequences. +# +def colorize(text, color, options): + if _DISPLAY_ == _DISPLAY_UNCOLORED_: + return text + + if options & OPTION_FLICKERING: + text = "" + text + if options & OPTION_BOLD: + text = "" + text + + if color == COLOR_BLACK: + text = "" + text + elif color == COLOR_RED: + text = "" + text + elif color == COLOR_GREEN: + text = "" + text + elif color == COLOR_YELLOW: + text = "" + text + elif color == COLOR_BLUE: + text = "" + text + elif color == COLOR_MAGENTA: + text = "" + text + elif color == COLOR_CYAN: + text = "" + text + elif color == COLOR_WHITE: + text = "" + text + + return text + "" + + + +# +# launch() +# +# this function launch a new program/script/make etc. +# +def launch(file, arguments, options): + directory = None + info = None + status = 0 + wd = None + + if options & OPTION_QUIET: + output = " >/dev/null 2>&1" + else: + output = "" + + info = os.path.split(file) + + directory = info[0] + file = info[1] + + if directory: + wd = cwd(OPTION_NONE) + cd(directory, OPTION_NONE) + + if re.match("^.*\.sh$", file): + status = os.system(_SHELL_ + " " + file + " " + arguments + output) + elif re.match("^.*\.py$", file): + status = os.system(_PYTHON_ + " " + file + " " + arguments + output) + elif re.match("^.*\.pl$", file): + status = os.system(_PERL_ + " " + file + " " + arguments + output) + elif re.match("^Makefile$", file): + status = os.system(_MAKE_ + " -f " + file + " " + arguments + output) + else: + status = os.system(file + " " + arguments + output) + + if directory: + cd(wd, OPTION_NONE) + + return status + + + +# +# pack() +# +# this function creates an archive of the given directory. +# +def pack(directory, file, options): + launch(_TAR_, "-cjf " + file + " " + directory, OPTION_NONE) + + + +# +# unpack() +# +# this function unpackages an archive into the given (optional) directory. +# +def unpack(file, directory, options): + if directory: + launch(_TAR_, "-xjf " + file + " -C " + directory, OPTION_NONE) + else: + launch(_TAR_, "-xjf " + file, OPTION_NONE) + + + +# +# load() +# +# this function copies a file on a device, this device can be virtual: +# an image. +# +def load(file, device, path, options): + if options == OPTION_DEVICE: + launch(_MCOPY_, "-o -n " + file + " " + device + path, OPTION_NONE) + + if options == OPTION_IMAGE: + launch(_MCOPY_, "-o -n " + "-i" + device + " " + + file + " ::" + path, OPTION_NONE) + + + +# +# record() +# +# this function runs the program recording a session. +# +def record(transcript, options): + directory = None + time = None + log = None + tmp = None + wd = None + + tmp = temporary(OPTION_DIRECTORY) + + directory = tmp + "/" + "transcript" + + log = directory + "/" + "log" + time = directory + "/" + "time" + + mkdir(directory, OPTION_NONE) + + launch(_SCRIPT_, "-q -t " + log + " -c " + + _TRANSCRIPT_CMD_ + " 2> " + time, OPTION_NONE) + + wd = cwd(OPTION_NONE) + + cd(tmp, OPTION_NONE) + + pack("transcript", wd + "/" + transcript, OPTION_NONE) + + cd(wd, OPTION_NONE) + + remove(tmp, OPTION_NONE) + + + +# +# play() +# +# this function runs the program replaying a session. +# +def play(transcript, options): + directory = None + time = None + log = None + tmp = None + wd = None + + tmp = temporary(OPTION_DIRECTORY) + + log = tmp + "/" + "transcript/log" + time = tmp + "/" + "transcript/time" + + unpack(transcript, tmp, OPTION_NONE) + + launch(_SCRIPTREPLAY_TOOL_, time + " " + log, OPTION_NONE) + + remove(tmp, OPTION_NONE) + + + +# +# locate() +# +# this function tries to locate a program on the system. +# +def locate(file, options): + return launch(_WHICH_, file + " 1>/dev/null 2>/dev/null", OPTION_NONE) diff --git a/environment/profile/host/darwin/ia32.ia32/educational b/environment/profile/host/darwin/ia32.ia32/educational new file mode 120000 index 0000000..945c9b4 --- /dev/null +++ b/environment/profile/host/darwin/ia32.ia32/educational @@ -0,0 +1 @@ +. \ No newline at end of file diff --git a/environment/profile/host/darwin/ia32.ia32/ia32.conf b/environment/profile/host/darwin/ia32.ia32/ia32.conf new file mode 100644 index 0000000..25f188e --- /dev/null +++ b/environment/profile/host/darwin/ia32.ia32/ia32.conf @@ -0,0 +1,33 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/fanfwe/kane...t/profile/host/darwin/ia32.ia32/ia32.conf +# +# created julien quintard [thu jun 28 21:15:33 2007] +# updated francois goudal [mon aug 4 15:57:00 2008] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file contains definitions specific to the ia32 target microprocessor +# architecture. +# + +# +# include the darwin generic definitions +# + +include ../darwin.conf + +# +# microprocessor specific definitions +# + +_CC_FLAGS_ += -D___kaneton$$\endian=0 \ + -D___kaneton$$\wordsz=32 \ + -D___kaneton$$\pagesz=4096 diff --git a/environment/profile/host/darwin/ia32.ia32/ia32.desc b/environment/profile/host/darwin/ia32.ia32/ia32.desc new file mode 120000 index 0000000..c6fdf08 --- /dev/null +++ b/environment/profile/host/darwin/ia32.ia32/ia32.desc @@ -0,0 +1 @@ +../darwin.desc \ No newline at end of file diff --git a/environment/profile/host/darwin/ia32.ia32/ia32.mk b/environment/profile/host/darwin/ia32.ia32/ia32.mk new file mode 120000 index 0000000..7efb456 --- /dev/null +++ b/environment/profile/host/darwin/ia32.ia32/ia32.mk @@ -0,0 +1 @@ +../darwin.mk \ No newline at end of file diff --git a/environment/profile/host/darwin/ia32.ia32/ia32.py b/environment/profile/host/darwin/ia32.ia32/ia32.py new file mode 120000 index 0000000..b470af6 --- /dev/null +++ b/environment/profile/host/darwin/ia32.ia32/ia32.py @@ -0,0 +1 @@ +../darwin.py \ No newline at end of file diff --git a/environment/profile/host/darwin/ia32.ia32/optimised b/environment/profile/host/darwin/ia32.ia32/optimised new file mode 120000 index 0000000..945c9b4 --- /dev/null +++ b/environment/profile/host/darwin/ia32.ia32/optimised @@ -0,0 +1 @@ +. \ No newline at end of file diff --git a/environment/profile/host/darwin/ia32.null/null.conf b/environment/profile/host/darwin/ia32.null/null.conf new file mode 100644 index 0000000..62cb959 --- /dev/null +++ b/environment/profile/host/darwin/ia32.null/null.conf @@ -0,0 +1,33 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/fanfwe/kane...t/profile/host/darwin/ia32.null/null.conf +# +# created julien quintard [thu jun 28 21:15:33 2007] +# updated francois goudal [mon aug 4 16:05:31 2008] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file contains definitions specific to the null target microprocessor +# architecture. +# + +# +# include the darwin generic definitions +# + +include ../darwin.conf + +# +# microprocessor specific definitions +# + +_CC_FLAGS_ += -D___kaneton$$\endian=0 \ + -D___kaneton$$\wordsz=32 \ + -D___kaneton$$\pagesz=1 diff --git a/environment/profile/host/darwin/ia32.null/null.desc b/environment/profile/host/darwin/ia32.null/null.desc new file mode 120000 index 0000000..c6fdf08 --- /dev/null +++ b/environment/profile/host/darwin/ia32.null/null.desc @@ -0,0 +1 @@ +../darwin.desc \ No newline at end of file diff --git a/environment/profile/host/darwin/ia32.null/null.mk b/environment/profile/host/darwin/ia32.null/null.mk new file mode 120000 index 0000000..7efb456 --- /dev/null +++ b/environment/profile/host/darwin/ia32.null/null.mk @@ -0,0 +1 @@ +../darwin.mk \ No newline at end of file diff --git a/environment/profile/host/darwin/ia32.null/null.py b/environment/profile/host/darwin/ia32.null/null.py new file mode 120000 index 0000000..b470af6 --- /dev/null +++ b/environment/profile/host/darwin/ia32.null/null.py @@ -0,0 +1 @@ +../darwin.py \ No newline at end of file diff --git a/environment/profile/host/host.conf b/environment/profile/host/host.conf new file mode 100644 index 0000000..a1b0009 --- /dev/null +++ b/environment/profile/host/host.conf @@ -0,0 +1,33 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/environment/profile/host/host.conf +# +# created julien quintard [tue may 8 13:03:21 2007] +# updated julien quintard [fri dec 17 20:04:01 2010] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file contains the default host variables. the different hosts +# should either complete or override them. +# + +# +# ---------- garbage ---------------------------------------------------------- +# + +_GARBAGE_ = *~ .*~ \#* .\#* *.pyc *.aux \ + *.log *.toc *.bbl *.blg *.nav \ + *.out *.snm *.vrb *.pdf + +# +# ---------- binaries --------------------------------------------------------- +# + +_BINARIES_ = diff --git a/environment/profile/host/host.desc b/environment/profile/host/host.desc new file mode 100644 index 0000000..a8a779f --- /dev/null +++ b/environment/profile/host/host.desc @@ -0,0 +1,18 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/environment/profile/host/host.desc +# +# created julien quintard [tue may 8 13:03:26 2007] +# updated julien quintard [sat may 19 14:30:26 2007] +# + +# +# ---------- information ------------------------------------------------------ +# +# XXX +# diff --git a/environment/profile/host/host.mk b/environment/profile/host/host.mk new file mode 100644 index 0000000..9159e1e --- /dev/null +++ b/environment/profile/host/host.mk @@ -0,0 +1,214 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton.STABLE/environment/profile/host/host.mk +# +# created julien quintard [tue may 8 13:03:34 2007] +# updated julien quintard [mon nov 29 10:18:58 2010] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file contains generic stuff about the implementation of the kaneton +# make interface. there are few functions here because make was not designed +# in a portable way. +# + +# +# ---------- directives ------------------------------------------------------- +# + +.SILENT: + +# +# ---------- options ---------------------------------------------------------- +# + +ENV_HEADER_NONE = 0 +ENV_HEADER_OK = 1 +ENV_HEADER_ERROR = 2 +ENV_HEADER_INTERACTIVE = 4 + +ENV_COLOR_NONE = 0 +ENV_COLOR_RED = 1 +ENV_COLOR_GREEN = 2 +ENV_COLOR_YELLOW = 3 +ENV_COLOR_BLUE = 4 +ENV_COLOR_WHITE = 5 + +ENV_OPTION_NONE = 0 +ENV_OPTION_NO_NEWLINE = 1 +ENV_OPTION_FLICKERING = 2 + +ENV_OPTION_NO_STANDARD = 1 +ENV_OPTION_STANDARD = 2 + +ENV_OPTION_PRIVATE = 1 + +# +# ---------- default rule ----------------------------------------------------- +# +# this rule is invoked when a Makefile is called without specifying a +# rule name. +# +# this rule is intended to check whether the configured environment files +# are up-to-date. if not, this rule re-builds the configured environment files. +# + +ENV_DEPENDENCIES := $(wildcard \ + $(_PROFILE_DIR_)/*.conf \ + $(_PROFILE_DIR_)/host/*.conf \ + $(_PROFILE_DIR_)/host/*.mk \ + $(_PROFILE_DIR_)/host/*.py \ + $(_PROFILE_HOST_DIR_)/*.conf \ + $(_PROFILE_HOST_DIR_)/*.mk \ + $(_PROFILE_HOST_DIR_)/*.py \ + $(_PROFILE_DIR_)/kaneton/*.conf \ + $(_PROFILE_DIR_)/kaneton/*.py \ + $(_PROFILE_DIR_)/kaneton/*.mk \ + $(_PROFILE_LIBRARY_DIR_)/*.conf \ + $(_PROFILE_LIBRARY_DIR_)/*.mk \ + $(_PROFILE_LIBRARY_DIR_)/*.py \ + $(_PROFILE_MODULES_DIR_)/*.conf \ + $(_PROFILE_MODULES_DIR_)/*.mk \ + $(_PROFILE_MODULES_DIR_)/*.py \ + $(_PROFILE_MACHINE_DIR_)/*.conf \ + $(_PROFILE_MACHINE_DIR_)/*.mk \ + $(_PROFILE_MACHINE_DIR_)/*.py \ + $(_PROFILE_PLATFORM_DIR_)/*.conf \ + $(_PROFILE_PLATFORM_DIR_)/*.mk \ + $(_PROFILE_PLATFORM_DIR_)/*.py \ + $(_PROFILE_ARCHITECTURE_DIR_)/*.conf \ + $(_PROFILE_ARCHITECTURE_DIR_)/*.mk \ + $(_PROFILE_ARCHITECTURE_DIR_)/*.py \ + $(_PROFILE_GLUE_DIR_)/*.conf \ + $(_PROFILE_GLUE_DIR_)/*.mk \ + $(_PROFILE_GLUE_DIR_)/*.py \ + $(_PROFILE_CORE_DIR_)/*.conf \ + $(_PROFILE_CORE_DIR_)/*.mk \ + $(_PROFILE_CORE_DIR_)/*.py \ + $(_PROFILE_DIR_)/user/*.conf \ + $(_PROFILE_DIR_)/user/*.mk \ + $(_PROFILE_DIR_)/user/*.py \ + $(_PROFILE_USER_DIR_)/*.conf \ + $(_PROFILE_USER_DIR_)/*.mk \ + $(_PROFILE_USER_DIR_)/*.py) + +_: $(_ENV_MK_) $(_ENV_PY_) + $(call env_launch,Makefile,main,) + +$(_ENV_MK_) $(_ENV_PY): \ + $(ENV_DEPENDENCIES) + $(call env_launch,$(_ENVIRONMENT_DIR_)/Makefile,initialize,) + +# +# ---------- traps ------------------------------------------------------------ +# + +%.o: %.S + $(call env_assemble-S,$@,$<,) + +%.o: %.c + $(call env_compile-c,$@,$<,) + +%.c: %.l + $(call env_lex-l,$@,$<,) + +# +# ---------- functions -------------------------------------------------------- +# + +# +# display functions +# +# 1: action +# 2: file +# 3: identation +# 4: options +# + +define env_display-red + $(call env_print,[,blue,$(ENV_OPTION_NO_NEWLINE)) && \ + $(call env_print,$(1),red,$(ENV_OPTION_NO_NEWLINE)) && \ + $(call env_print,],blue,$(ENV_OPTION_NO_NEWLINE)) && \ + $(call env_print,$(3)$(2),,) +endef + +define env_display-green + $(call env_print,[,blue,$(ENV_OPTION_NO_NEWLINE)) && \ + $(call env_print,$(1),green,$(ENV_OPTION_NO_NEWLINE)) && \ + $(call env_print,],blue,$(ENV_OPTION_NO_NEWLINE)) && \ + $(call env_print,$(3)$(2),,) +endef + +define env_display-yellow + $(call env_print,[,blue,$(ENV_OPTION_NO_NEWLINE)) && \ + $(call env_print,$(1),yellow,$(ENV_OPTION_NO_NEWLINE)) && \ + $(call env_print,],blue,$(ENV_OPTION_NO_NEWLINE)) && \ + $(call env_print,$(3)$(2),,) +endef + +define env_display-magenta + $(call env_print,[,blue,$(ENV_OPTION_NO_NEWLINE)) && \ + $(call env_print,$(1),magenta,$(ENV_OPTION_NO_NEWLINE)) && \ + $(call env_print,],blue,$(ENV_OPTION_NO_NEWLINE)) && \ + $(call env_print,$(3)$(2),,) +endef + +define env_display-cyan + $(call env_print,[,blue,$(ENV_OPTION_NO_NEWLINE)) && \ + $(call env_print,$(1),cyan,$(ENV_OPTION_NO_NEWLINE)) && \ + $(call env_print,],blue,$(ENV_OPTION_NO_NEWLINE)) && \ + $(call env_print,$(3)$(2),,) +endef + +# +# display wrapper +# +# $(1): color +# $(2): action +# $(3): file +# $(4): indentation +# $(5): options +# + +define env_display + $(call env_display-$(1),$(2),$(3),$(4),$(5)) +endef + +# +# ---------- information ------------------------------------------------------ +# + +directory := $(CURDIR) + +# +# ---------- component-based behaviour ---------------------------------------- +# + +# +# behaviour +# + +behaviour := default + +# +# components +# + +ifeq ($(call findstring,$(component),$(components)),) + components += $(component) +endif + +export components + +# +# ---------- cc flags --------------------------------------------------------- +# + +_CC_FLAGS_ += $(foreach m,$(_MODULES_),-DMODULE_$(m)) diff --git a/environment/profile/host/host.py b/environment/profile/host/host.py new file mode 100644 index 0000000..351a111 --- /dev/null +++ b/environment/profile/host/host.py @@ -0,0 +1,382 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/environment/profile/host/host.py +# +# created julien quintard [tue may 8 13:03:40 2007] +# updated julien quintard [mon mar 7 16:36:27 2011] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file contains generic stuff about the python interface including +# options and general functions. +# +# since the python scripting language was designed to provide portable +# operations, the following operations are considered as generic, making +# work easier for host sub-profiles. +# + +# +# ---------- imports ---------------------------------------------------------- +# + +import os +import re +import sys +import tempfile +import shutil +import time + +# +# ---------- options ---------------------------------------------------------- +# + +HEADER_NONE = 0 +HEADER_OK = 1 +HEADER_ERROR = 2 +HEADER_INTERACTIVE = 4 + +COLOR_NONE = 0 +COLOR_BLACK = 1 +COLOR_RED = 2 +COLOR_GREEN = 3 +COLOR_YELLOW = 4 +COLOR_BLUE = 5 +COLOR_MAGENTA = 6 +COLOR_CYAN = 7 +COLOR_WHITE = 8 + +OPTION_NONE = 0 + +OPTION_QUIET = 1 + +OPTION_NO_NEWLINE = 1 +OPTION_FLICKERING = 2 +OPTION_BOLD = 4 + +OPTION_FILE = 1 +OPTION_DIRECTORY = 2 +OPTION_LINK = 4 +OPTION_RECURSIVE = 8 +OPTION_EXIST = 16 +OPTION_HIDDEN = 32 +OPTION_ALL = OPTION_FILE | OPTION_DIRECTORY | OPTION_LINK + +OPTION_DEVICE = 1 +OPTION_IMAGE = 2 + +OPTION_CURRENT_DIRECTORY = 1 + +# +# ---------- functions -------------------------------------------------------- +# + +# +# display() +# +# this function prints a kaneton message. +# +def display(header, text, options): + if header == HEADER_NONE: + sys.stdout.write(colorize(text, COLOR_NONE, OPTION_NONE)) + elif header == HEADER_OK: + sys.stdout.write(colorize("[", COLOR_BLUE, OPTION_BOLD)) + sys.stdout.write(colorize("+", COLOR_GREEN, OPTION_BOLD)) + sys.stdout.write(colorize("]", COLOR_BLUE, OPTION_BOLD)) + sys.stdout.write(" ") + sys.stdout.write(colorize(text, COLOR_NONE, OPTION_NONE)) + elif header == HEADER_ERROR: + sys.stdout.write(colorize("[", COLOR_BLUE, OPTION_BOLD)) + sys.stdout.write(colorize("!", COLOR_RED, OPTION_BOLD)) + sys.stdout.write(colorize("]", COLOR_BLUE, OPTION_BOLD)) + sys.stdout.write(" ") + sys.stdout.write(colorize(text, COLOR_NONE, OPTION_NONE)) + elif header == HEADER_INTERACTIVE: + sys.stdout.write(colorize("[", COLOR_BLUE, OPTION_BOLD)) + sys.stdout.write(colorize("?", + COLOR_YELLOW, + OPTION_FLICKERING | OPTION_BOLD)) + sys.stdout.write(colorize("]", COLOR_BLUE, OPTION_BOLD)) + sys.stdout.write(" ") + sys.stdout.write(colorize(text, COLOR_NONE, OPTION_NONE)) + + if not options & OPTION_NO_NEWLINE: + sys.stdout.write("\n") + + + +# +# pull() +# +# this function reads the content of a single file. +# +def pull(file, options): + handle = None + line = None + + if not os.path.exists(file): + return None + + try: + handle = open(file, "r") + except IOError: + return None + + content = "" + for line in handle.readlines(): + content += line + + handle.close() + + return content + + + +# +# push() +# +# this function writes the content of a single file. +# +def push(file, content, options): + handle = None + + handle = open(file, "w") + + handle.write(content) + + handle.close() + + + +# +# temporary() +# +# this function provides an easy way to create a temporary file or directory. +# +def temporary(options): + location = None + + if options == OPTION_FILE: + tuple = tempfile.mkstemp() + + os.close(tuple[0]) + + location = tuple[1] + elif options == OPTION_DIRECTORY: + location = tempfile.mkdtemp() + + return location + + + +# +# cwd() +# +# this function returns the current working directory. +# +def cwd(options): + return os.getcwd() + + + +# +# input() +# +# this function waits for an input. +# +def input(options): + return input() + + + +# +# copy() +# +# this function copies a file or a directory. +# +def copy(source, destination, options): + if os.path.isdir(source): + shutil.copytree(source, destination, True) + else: + shutil.copy2(source, destination) + + + +# +# move() +# +# this function moves a file or a directory. +# +def move(source, destination, options): + copy(source, destination, options) + remove(source, options) + + + +# +# link() +# +# this function builds a link name source to the file destination. +# +def link(source, destination, options): + os.symlink(destination, source) + + + +# +# remove() +# +# this function removes the targets. +# +def remove(target, options): + entries = None + entry = None + + if os.path.isfile(target) or os.path.islink(target): + os.unlink(target) + + if os.path.isdir(target): + entries = os.listdir(target) + for entry in entries: + remove(target + "/" + entry, OPTION_NONE) + os.rmdir(target) + + + +# +# list() +# +# this function lists the entries of a directory. +# +def list(directory, options): + elements = [] + entries = None + entry = None + + entries = os.listdir(directory) + + for entry in entries: + if not (options & OPTION_HIDDEN) and \ + (entry[0:1] == "."): + continue + if (options & OPTION_LINK) and \ + (os.path.islink(directory + "/" + entry)): + elements += [ entry ] + if (options & OPTION_FILE) and \ + (os.path.isfile(directory + "/" + entry)) and \ + (not os.path.islink(directory + "/" + entry)): + elements += [ entry ] + if (options & OPTION_DIRECTORY) and \ + (os.path.isdir(directory + "/" + entry)) and \ + (not os.path.islink(directory + "/" + entry)): + elements += [ entry ] + + return elements + + + +# +# cd() +# +# this function changes the current working directory. +# +def cd(directory, options): + os.chdir(directory) + + + +# +# search() +# +# this function searches for file names matching the given pattern. +# +def search(directory, pattern, options): + elements = [] + entries = None + entry = None + + entries = os.listdir(directory) + + for entry in entries: + if (options & OPTION_FILE) and \ + (os.path.isfile(directory + "/" + entry)) and \ + (re.search(pattern, entry)): + elements += [ directory + "/" + entry ] + + if (os.path.isdir(directory + "/" + entry)): + if (options & OPTION_DIRECTORY) and \ + (re.search(pattern, entry)): + elements += [ directory + "/" + entry ] + + if (options & OPTION_RECURSIVE) and \ + (not os.path.islink(directory + "/" + entry)): + elements += search(directory + "/" + entry, pattern, options) + + return elements + + + +# +# mkdir() +# +# this function creates a directory. +# +def mkdir(directory, options): + path = None + steps = None + step = None + + steps = directory.strip("/").split("/") + + for step in steps: + if not path: + path = "/" + step + else: + path = path + "/" + step + + if not os.path.exists(path): + os.mkdir(path) + + + +# +# stamp() +# +# this function returns the current formatted date. +# +def stamp(options): + return time.strftime("%Y%m%d") + + + +# +# path() +# +# this function returns information on a path: file, directory etc. +# +def path(path, options): + if options == OPTION_FILE: + return os.path.basename(path) + if options == OPTION_DIRECTORY: + return os.path.dirname(path) + if options == OPTION_EXIST: + return os.path.exists(path) + + return None + + + +# +# info() +# +# this function returns information on the system. +# +def info(options): + if (options & OPTION_CURRENT_DIRECTORY): + return os.path.curdir diff --git a/environment/profile/host/linux/ia32.ia32/educational b/environment/profile/host/linux/ia32.ia32/educational new file mode 120000 index 0000000..945c9b4 --- /dev/null +++ b/environment/profile/host/linux/ia32.ia32/educational @@ -0,0 +1 @@ +. \ No newline at end of file diff --git a/environment/profile/host/linux/ia32.ia32/ia32.conf b/environment/profile/host/linux/ia32.ia32/ia32.conf new file mode 120000 index 0000000..35a74d6 --- /dev/null +++ b/environment/profile/host/linux/ia32.ia32/ia32.conf @@ -0,0 +1 @@ +../linux.conf \ No newline at end of file diff --git a/environment/profile/host/linux/ia32.ia32/ia32.desc b/environment/profile/host/linux/ia32.ia32/ia32.desc new file mode 120000 index 0000000..2b67745 --- /dev/null +++ b/environment/profile/host/linux/ia32.ia32/ia32.desc @@ -0,0 +1 @@ +../linux.desc \ No newline at end of file diff --git a/environment/profile/host/linux/ia32.ia32/ia32.mk b/environment/profile/host/linux/ia32.ia32/ia32.mk new file mode 120000 index 0000000..3ee4ae2 --- /dev/null +++ b/environment/profile/host/linux/ia32.ia32/ia32.mk @@ -0,0 +1 @@ +../linux.mk \ No newline at end of file diff --git a/environment/profile/host/linux/ia32.ia32/ia32.py b/environment/profile/host/linux/ia32.ia32/ia32.py new file mode 120000 index 0000000..b70e086 --- /dev/null +++ b/environment/profile/host/linux/ia32.ia32/ia32.py @@ -0,0 +1 @@ +../linux.py \ No newline at end of file diff --git a/environment/profile/host/linux/ia32.ia32/optimised b/environment/profile/host/linux/ia32.ia32/optimised new file mode 120000 index 0000000..945c9b4 --- /dev/null +++ b/environment/profile/host/linux/ia32.ia32/optimised @@ -0,0 +1 @@ +. \ No newline at end of file diff --git a/environment/profile/host/linux/ia32.mips64/mips64.conf b/environment/profile/host/linux/ia32.mips64/mips64.conf new file mode 100644 index 0000000..7e50f3c --- /dev/null +++ b/environment/profile/host/linux/ia32.mips64/mips64.conf @@ -0,0 +1,137 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kane...ofile/host/linux/ia32.mips64/mips64.conf +# +# created julien quintard [thu jun 28 21:15:33 2007] +# updated julien quintard [fri may 1 23:54:21 2009] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file contains definitions specific to the mips64 target microprocessor +# architecture. +# + +# +# include the linux generic definitions +# + +_INCLUDES_ = -I${_INCLUDE_DIR_} + +# +# ---------- flags ------------------------------------------------------------ +# + +_CC_FLAGS_ = -D___kaneton \ + ${_INCLUDES_} \ + ${_KANETON_FLAGS_} \ + -fno-builtin \ + -Wimplicit \ + -Wparentheses \ + -Wreturn-type \ + -Wswitch -Wswitch-enum \ + -Wunused-function \ + -Wunused-variable \ + -Wmissing-prototypes \ + -Wmissing-declarations \ + -Wall -W -std=c99 \ + -mabi=64 \ + -G 0 \ + -march=mips64 \ + -EL \ + -mgp64 \ + -mfp64 \ + -mlong64 \ + -DMIPS64_DEPENDENT \ + -DMIPS64_DEV + +_LD_FLAGS_ = --warn-unresolved-symbols \ + --oformat=elf64-littlemips + +_ASM_FLAGS_ = -mabi=64 \ + -G 0 \ + -march=mips64 \ + -EL \ + -mgp64 \ + -mfp64 + +_CPP_FLAGS_ = + +_SHELL_FLAGS_ = + +_PYTHON_FLAGS_ = + +_PERL_FLAGS_ = + +_MAKE_FLAGS_ = + +# +# ---------- binaries --------------------------------------------------------- +# + +_BINARIES_ = ${_SHELL_}, ${_CC_}, ${_MAKE_}, \ + ${_RM_}, ${_AR_}, ${_RANLIB_}, \ + ${_LD_}, ${_AS_}, ${_BIBTEX_},\ + ${_LN_}, ${_TOUCH_}, ${_WC_}, \ + ${_DATE_}, ${_TAIL_}, ${_TAR_}, \ + ${_PDFLATEX_}, ${_CP_}, \ + ${_CAT_}, ${_SED_}, ${_CPP_}, \ + ${_MTOOLS_}, ${_MCOPY_}, \ + ${_XPDF_}, ${_MKTEMP_}, ${_MV_},\ + ${_LEX_}, ${_SCRIPT_}, \ + ${_PERL_}, ${_PYTHON_}, \ + ${_DIRNAME_}, ${_BASENAME_}, \ + ${_WHICH_} + +_SHELL_ = bash +_CC_ = mips64-gcc +_AS_ = mips64-as +_OBJCOPY_ = mips64-objcopy +_MAKE_ = gmake +_RM_ = rm -f +_PURGE_ = ${_RM_} *.pyc *~ .*~ \#* .\#* +_AR_ = mips64-ar cq +_RANLIB_ = mips64-ranlib +_CD_ = cd +_LD_ = mips64-ld +_LN_ = ln -s -f +_TOUCH_ = touch +_WC_ = wc +_DATE_ = date -u +_TAIL_ = tail +_TAR_ = tar +_PDFLATEX_ = pdflatex +_BIBTEX_ = bibtex +_CP_ = cp +_CAT_ = cat +_SED_ = sed -r +_ECHO_ = echo +_CPP_ = mips64-cpp +_MTOOLS_ = mtools +_MCOPY_ = mcopy +_XPDF_ = xpdf +_MKTEMP_ = mktemp +_MV_ = mv +_PWD_ = pwd +_LEX_ = lex -t +_SCRIPT_ = script +_PERL_ = perl +_PYTHON_ = python +_DIRNAME_ = dirname +_BASENAME_ = basename +_WHICH_ = which +_EXIT_ = exit + +# +# microprocessor specific definitions +# + +_CC_FLAGS_ += -D___kaneton$$\endian=1 \ + -D___kaneton$$\wordsz=64 \ + -D___kaneton$$\pagesz=4096 diff --git a/environment/profile/host/linux/ia32.mips64/mips64.desc b/environment/profile/host/linux/ia32.mips64/mips64.desc new file mode 100644 index 0000000..afbba12 --- /dev/null +++ b/environment/profile/host/linux/ia32.mips64/mips64.desc @@ -0,0 +1,33 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kane...nvironment/profile/host/linux/linux.desc +# +# created julien quintard [tue may 8 13:20:04 2007] +# updated julien quintard [tue may 29 16:37:31 2007] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file describes the linux environment variables. +# + +- variable: _SHELL_ + string: Shell + type: any + description: The shell binary path + +- variable: _CC_ + string: C Compiler + type: any + description: The C compiler binary path + +- variable: _MAKE_ + string: Make + type: any + description: The make binary path diff --git a/environment/profile/host/linux/ia32.mips64/mips64.mk b/environment/profile/host/linux/ia32.mips64/mips64.mk new file mode 100644 index 0000000..bf48518 --- /dev/null +++ b/environment/profile/host/linux/ia32.mips64/mips64.mk @@ -0,0 +1,23 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/enguerrand/...profile/host/linux/ia32.mips64/mips64.mk +# +# created julien quintard [tue may 8 13:03:34 2007] +# updated enguerrand raymond [sun may 17 09:36:48 2009] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file implements the remaining functions of the kaneton make interface. +# +# indeed the major generic part of the interface is already provided by the +# host profile. +# + +include ../linux.mk diff --git a/environment/profile/host/linux/ia32.mips64/mips64.py b/environment/profile/host/linux/ia32.mips64/mips64.py new file mode 100644 index 0000000..2a41681 --- /dev/null +++ b/environment/profile/host/linux/ia32.mips64/mips64.py @@ -0,0 +1,219 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/enguerrand/...profile/host/linux/ia32.mips64/mips64.py +# +# created julien quintard [tue may 8 13:20:21 2007] +# updated enguerrand raymond [fri apr 17 20:59:36 2009] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file implements the remaining functions of the kaneton python interface. +# +# note that the host profile already provides many functions. these +# functions can be overriden but you will probably just use them. +# +# in addition, the host profile already imports some packages. +# + +# +# ---------- python path ------------------------------------------------------ +# + +pythonpath = os.getenv("PYTHONPATH") +if not pythonpath: + pythonpath = "" + +os.putenv("PYTHONPATH", pythonpath + ":" + _PYTHON_INCLUDE_DIR_) + +# +# ---------- functions -------------------------------------------------------- +# + +# +# colorize() +# +# this function returns a colorized text if the environment is configured +# to or simply the original text. +# +# note that this function implementation is based on UNIX escape sequences. +# +def colorize(text, color, options): + if _DISPLAY_ == _DISPLAY_UNCOLORED_: + return text + + if options & OPTION_FLICKERING: + text = "" + text + "" + + if color == COLOR_BLACK: + text = "" + text + "" + elif color == COLOR_RED: + text = "" + text + "" + elif color == COLOR_GREEN: + text = "" + text + "" + elif color == COLOR_YELLOW: + text = "" + text + "" + elif color == COLOR_BLUE: + text = "" + text + "" + elif color == COLOR_MAGENTA: + text = "" + text + "" + elif color == COLOR_CYAN: + text = "" + text + "" + elif color == COLOR_WHITE: + text = "" + text + "" + + return text + + + +# +# launch() +# +# this function launch a new program/script/make etc. +# +def launch(file, arguments, options): + directory = None + info = None + status = 0 + wd = None + + info = os.path.split(file) + + directory = info[0] + file = info[1] + + if directory: + wd = cwd(OPTION_NONE) + cd(directory, OPTION_NONE) + + if re.match("^.*\.sh$", file): + status = os.system(_SHELL_ + " " + file + " " + arguments) + elif re.match("^.*\.py$", file): + status = os.system(_PYTHON_ + " " + file + " " + arguments) + elif re.match("^.*\.pl$", file): + status = os.system(_PERL_ + " " + file + " " + arguments) + elif re.match("^Makefile$", file): + status = os.system(_MAKE_ + " -f " + file + " " + arguments) + else: + status = os.system(file + " " + arguments) + + if directory: + cd(wd, OPTION_NONE) + + return status + + + +# +# pack() +# +# this function creates an archive of the given directory. +# +def pack(directory, file, options): + launch(_TAR_, "-cjf " + file + " " + directory, OPTION_NONE) + + + +# +# unpack() +# +# this function unpackages an archive into the given (optional) directory. +# +def unpack(file, directory, options): + if directory: + launch(_TAR_, "-xjf " + file + " -C " + directory, OPTION_NONE) + else: + launch(_TAR_, "-xjf " + file, OPTION_NONE) + + + +# +# load() +# +# this function copies a file on a device, this device can be virtual: +# an image. +# +def load(file, device, path, options): + if options == OPTION_DEVICE: + launch(_MCOPY_, "-o -n " + file + " " + device + path, OPTION_NONE) + + if options == OPTION_IMAGE: + launch(_MCOPY_, "-o -n " + "-i" + device + " " + + file + " ::" + path, OPTION_NONE) + + + +# +# record() +# +# this function runs the program recording a session. +# +def record(transcript, options): + directory = None + time = None + log = None + tmp = None + wd = None + + tmp = temporary(OPTION_DIRECTORY) + + directory = tmp + "/" + "transcript" + + log = directory + "/" + "log" + time = directory + "/" + "time" + + mkdir(directory, OPTION_NONE) + + launch(_SCRIPT_, "-q -t " + log + " -c " + + _TRANSCRIPT_CMD_ + " 2> " + time, OPTION_NONE) + + wd = cwd(OPTION_NONE) + + cd(tmp, OPTION_NONE) + + pack("transcript", wd + "/" + transcript, OPTION_NONE) + + cd(wd, OPTION_NONE) + + remove(tmp, OPTION_NONE) + + + +# +# play() +# +# this function runs the program replaying a session. +# +def play(transcript, options): + directory = None + time = None + log = None + tmp = None + wd = None + + tmp = temporary(OPTION_DIRECTORY) + + log = tmp + "/" + "transcript/log" + time = tmp + "/" + "transcript/time" + + unpack(transcript, tmp, OPTION_NONE) + + launch(_SCRIPTREPLAY_TOOL_, time + " " + log, OPTION_NONE) + + remove(tmp, OPTION_NONE) + + + +# +# locate() +# +# this function tries to locate a program on the system. +# +def locate(file, options): + return launch(_WHICH_, file + " 1>/dev/null 2>/dev/null", OPTION_NONE) diff --git a/environment/profile/host/linux/ia32.mips64/util.py b/environment/profile/host/linux/ia32.mips64/util.py new file mode 100644 index 0000000..4831bc7 --- /dev/null +++ b/environment/profile/host/linux/ia32.mips64/util.py @@ -0,0 +1,48 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/enguerrand/...t/profile/host/linux/ia32.mips64/util.py +# +# created enguerrand raymond [fri apr 17 20:59:04 2009] +# updated enguerrand raymond [fri apr 17 21:00:47 2009] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file implements some useful functions used for mips compilation, build +# and installation. +# + + + +# +# binary_extract() +# +# this function extracts given sections (section names separate by space) +# from elf to put in binary +# +def binary_extract(elf, sections, binary): + section_list = sections.split() + cmd_option = "-S" + + for section in section_list: + cmd_option += " -j " + section + + cmd_option += " --output-target binary " + elf + " " + binary + + launch(_OBJCOPY_, cmd_option, "") + + + +# +# concat_file() +# +# this function concatenates file a the result file end +# +def concat_file(file, result): + os.system(_CAT_ + " " + file + " >> " + result) diff --git a/environment/profile/host/linux/ia32.null/null.conf b/environment/profile/host/linux/ia32.null/null.conf new file mode 100644 index 0000000..72905d7 --- /dev/null +++ b/environment/profile/host/linux/ia32.null/null.conf @@ -0,0 +1,33 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kane...t/profile/host/linux/ia32.null/null.conf +# +# created julien quintard [thu jun 28 21:15:33 2007] +# updated julien quintard [wed mar 26 23:37:31 2008] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file contains definitions specific to the null target microprocessor +# architecture. +# + +# +# include the linux generic definitions +# + +include ../linux.conf + +# +# microprocessor specific definitions +# + +_CC_FLAGS_ += -D___kaneton$$\endian=0 \ + -D___kaneton$$\wordsz=32 \ + -D___kaneton$$\pagesz=1 diff --git a/environment/profile/host/linux/ia32.null/null.desc b/environment/profile/host/linux/ia32.null/null.desc new file mode 120000 index 0000000..2b67745 --- /dev/null +++ b/environment/profile/host/linux/ia32.null/null.desc @@ -0,0 +1 @@ +../linux.desc \ No newline at end of file diff --git a/environment/profile/host/linux/ia32.null/null.mk b/environment/profile/host/linux/ia32.null/null.mk new file mode 120000 index 0000000..3ee4ae2 --- /dev/null +++ b/environment/profile/host/linux/ia32.null/null.mk @@ -0,0 +1 @@ +../linux.mk \ No newline at end of file diff --git a/environment/profile/host/linux/ia32.null/null.py b/environment/profile/host/linux/ia32.null/null.py new file mode 120000 index 0000000..b70e086 --- /dev/null +++ b/environment/profile/host/linux/ia32.null/null.py @@ -0,0 +1 @@ +../linux.py \ No newline at end of file diff --git a/environment/profile/host/linux/linux.conf b/environment/profile/host/linux/linux.conf new file mode 100644 index 0000000..e51647d --- /dev/null +++ b/environment/profile/host/linux/linux.conf @@ -0,0 +1,113 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kane...nvironment/profile/host/linux/linux.conf +# +# created julien quintard [tue may 8 13:19:52 2007] +# updated julien quintard [mon feb 7 10:36:01 2011] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file contains the libraries, binaries, script and tools related to +# the generic linux host sub-profile. +# + +# +# ---------- includes --------------------------------------------------------- +# + +_INCLUDES_ = -I${_INCLUDE_DIR_} + +# +# ---------- flags ------------------------------------------------------------ +# + +_CC_FLAGS_ = ${_INCLUDES_} \ + ${_KANETON_FLAGS_} \ + -fno-builtin \ + -fno-stack-protector \ + -Wimplicit \ + -Wparentheses \ + -Wreturn-type \ + -Wswitch -Wswitch-enum \ + -Wunused-function \ + -Wunused-variable \ + -Wmissing-prototypes \ + -Wmissing-declarations \ + -Wall + +_LD_FLAGS_ = ${_INCLUDES_} + +_ASM_FLAGS_ = + +_CPP_FLAGS_ = + +_SHELL_FLAGS_ = + +_PYTHON_FLAGS_ = + +_PERL_FLAGS_ = + +_MAKE_FLAGS_ = + +# +# ---------- binaries --------------------------------------------------------- +# + +_BINARIES_ = ${_SHELL_}, ${_CC_}, ${_MAKE_}, \ + ${_RM_}, ${_AR_}, ${_RANLIB_}, \ + ${_LD_}, ${_AS_}, ${_BIBTEX_},\ + ${_LN_}, ${_TOUCH_}, ${_WC_}, \ + ${_DATE_}, ${_TAIL_}, ${_TAR_}, \ + ${_PDFLATEX_}, ${_CP_}, \ + ${_CAT_}, ${_SED_}, ${_CPP_}, \ + ${_MTOOLS_}, ${_MCOPY_}, \ + ${_XPDF_}, ${_MKTEMP_}, ${_MV_},\ + ${_LEX_}, ${_SCRIPT_}, \ + ${_PERL_}, ${_PYTHON_}, \ + ${_DIRNAME_}, ${_BASENAME_}, \ + ${_WHICH_} + +_SHELL_ = bash +_CC_ = gcc +_AS_ = as +_MAKE_ = make +_RM_ = rm -f +_PURGE_ = ${_RM_} ${_GARBAGE_} +_AR_ = ar cq +_RANLIB_ = ranlib +_CD_ = cd +_LD_ = ld +_LN_ = ln -s -f +_TOUCH_ = touch +_WC_ = wc +_DATE_ = date -u +_TAIL_ = tail +_TAR_ = tar +_PDFLATEX_ = pdflatex +_BIBTEX_ = bibtex +_CP_ = cp +_CAT_ = cat +_SED_ = sed -r +_ECHO_ = echo +_CPP_ = cpp +_MTOOLS_ = mtools +_MCOPY_ = mcopy +_XPDF_ = xpdf +_MKTEMP_ = mktemp +_MV_ = mv +_PWD_ = pwd +_LEX_ = lex -t +_SCRIPT_ = script +_PERL_ = perl +_PYTHON_ = python +_DIRNAME_ = dirname +_BASENAME_ = basename +_WHICH_ = which +_EXIT_ = exit diff --git a/environment/profile/host/linux/linux.desc b/environment/profile/host/linux/linux.desc new file mode 100644 index 0000000..afbba12 --- /dev/null +++ b/environment/profile/host/linux/linux.desc @@ -0,0 +1,33 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kane...nvironment/profile/host/linux/linux.desc +# +# created julien quintard [tue may 8 13:20:04 2007] +# updated julien quintard [tue may 29 16:37:31 2007] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file describes the linux environment variables. +# + +- variable: _SHELL_ + string: Shell + type: any + description: The shell binary path + +- variable: _CC_ + string: C Compiler + type: any + description: The C compiler binary path + +- variable: _MAKE_ + string: Make + type: any + description: The make binary path diff --git a/environment/profile/host/linux/linux.mk b/environment/profile/host/linux/linux.mk new file mode 100644 index 0000000..f03bc7c --- /dev/null +++ b/environment/profile/host/linux/linux.mk @@ -0,0 +1,507 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kane.../environment/profile/host/linux/linux.mk +# +# created julien quintard [tue may 8 13:03:34 2007] +# updated julien quintard [sun dec 5 00:02:41 2010] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file implements the remaining functions of the kaneton make interface. +# +# indeed the major generic part of the interface is already provided by the +# host profile. +# + +# +# ---------- lang ------------------------------------------------------------- +# +# incorrect locales may prevent the system from compiling properly. +# +# the following forces the LANG environment variable to US. +# +export LANG=US + +# +# ---------- python path ------------------------------------------------------ +# + +export PYTHONPATH=$(shell $(_ECHO_) $${PYTHONPATH}):$(_PYTHON_INCLUDE_DIR_) + +# +# ---------- functions -------------------------------------------------------- +# + +# +# colorize functions +# +# 1: text +# 2: options +# + +define env_colorize-black + "$(1)" +endef + +define env_colorize-red + "$(1)" +endef + +define env_colorize-green + "$(1)" +endef + +define env_colorize-yellow + "$(1)" +endef + +define env_colorize-blue + "$(1)" +endef + +define env_colorize-magenta + "$(1)" +endef + +define env_colorize-cyan + "$(1)" +endef + +define env_colorize-white + "$(1)" +endef + +define env_colorize- + "$(1)" +endef + +# +# perform function +# +# 1: command +# + +ifeq ($(_OUTPUT_),$(_OUTPUT_VERBOSE_)) # if the user wants to display + # additional debug information + +define env_perform + $(_ECHO_) " $(1)" && \ + $(1) +endef + +else + +define env_perform + $(1) +endef + +endif + +# +# print functions wrapper +# +# 1: text +# 2: color +# 3: options +# + +ifeq ($(_DISPLAY_),$(_DISPLAY_COLORED_)) # if the user wants to display + # the text with color + +define env_print + print_options="" && \ + if [ -n "$(3)" ] ; then \ + if [ $$(( $(3) & $(ENV_OPTION_NO_NEWLINE) )) -ne 0 ] ; then \ + print_options="$${print_options} -n" ; \ + fi ; \ + fi && \ + $(_ECHO_) $${print_options} $(call env_colorize-$(2),$(1),) +endef + +else # if not ... + +define env_print + print_options="" && \ + if [ -n "$(3)" ] ; then \ + if [ $$(( $(3) & $(ENV_OPTION_NO_NEWLINE) )) -ne 0 ] ; then \ + print_options="$${print_options} -n" ; \ + fi ; \ + fi && \ + $(_ECHO_) $${print_options} "$(1)" +endef + +endif + +# +# change the current working directory +# +# $(1): directory +# $(2): options +# + +define env_cd + cd_options="" && \ + $(call env_perform, \ + $(_CD_) $${cd_options} $(1)) +endef + +# +# returns a file contents +# +# $(1): the file +# $(2): options +# + +define env_pull + $(_CAT_) $(2) $(1) +endef + +# +# launch a new program/script/make etc. +# +# $(1): file +# $(2): arguments +# $(3): options +# + +define env_launch + launch_options="" && \ + cwd=$$($(_PWD_)) && \ + directory=$$($(_DIRNAME_) $(1)) && \ + file=$$($(_BASENAME_) $(1)) && \ + if [ "$${directory}" != "." ] ; then \ + $(call env_perform, \ + $(_CD_) $${directory}) ; \ + fi && \ + case "$${file}" in \ + *.sh) \ + $(call env_perform, \ + $(_SHELL_) $${launch_options} $${file} $(_SHELL_FLAGS_) $(2)) ; \ + ;; \ + *.py) \ + $(call env_perform, \ + $(_PYTHON_) $${launch_options} $${file} $(_PYTHON_FLAGS_) $(2)) ; \ + ;; \ + *.pl) \ + $(call env_perform, \ + $(_PERL_) $${launch_options} $${file} $(_PERL_FLAGS_) $(2)) ; \ + ;; \ + Makefile) \ + $(call env_perform, \ + $(_MAKE_) $${launch_options} -f $${file} $(_MAKE_FLAGS_) ${2}) ; \ + ;; \ + esac ; \ + return=$${?} && \ + if [ "$${directory}" != "." ] ; then \ + $(call env_perform, \ + $(_CD_) $${cwd}) ; \ + fi && \ + if [ $${return} -ne 0 ] ; then \ + $(_EXIT_) 42 ; \ + fi +endef + +# +# from c file to preprocessed c file +# +# $(1): preprocessed file +# $(2): c file +# $(3): options +# + +define env_preprocess + preprocess_options="" && \ + $(call env_display,green,PREPROCESS,$(2), ,) && \ + $(call env_perform, \ + $(_CPP_) -P $(_CPP_FLAGS_) $${preprocess_options} $(2) -o $(1)) +endef + +# +# from c file to object file +# +# $(1): object file +# $(2): c file +# $(3): options +# + +define env_compile-c + compile_c_options="" && \ + $(call env_display,green,COMPILE-C,$(2), ,) && \ + $(call env_perform, \ + $(_CC_) $(_CC_FLAGS_) $${compile_c_options} -c $(2) -o $(1)) +endef + +# +# from lex file to c file +# +# $(1): c file +# $(2): lex file +# $(3): options +# + +define env_lex-l + lex_l_options="" && \ + $(call env_display,green,LEX-L,$(2), ,) && \ + $(call env_perform, \ + $(_LEX_) $${lex_l_options} $(2) > $(1)) +endef + +# +# from S file to object file +# +# $(1): object file +# $(2): S file +# $(3): options +# + +define env_assemble-S + assemble_S_options="" && \ + $(call env_display,green,ASSEMBLE-S,$(2), ,) && \ + $(call env_perform, \ + $(_CPP_) $(2) | $(_AS_) $(_ASM_FLAGS_) $${assemble_S_options} -o $(1)) +endef + +# +# create a static library from object files +# +# $(1): static library file name +# $(2): object files +# $(3): options +# + +define env_static-library + static_library_options="" && \ + $(call env_display,magenta,STATIC-LIBRARY,$(1), ,) && \ + $(call env_perform, \ + $(_AR_) $${static_library_options} $(1) $(2)) && \ + $(call env_perform, \ + $(_RANLIB_) $${static_library_options} $(1)) +endef + +# +# create a dynamic library from object files, static libraries +# and/or dynamic libraries +# +# $(1): dynamic library file name +# $(2): objects files and/or libraries +# $(3): options +# + +define env_dynamic-library + dynamic_library_options="" && \ + $(call env_display,magenta,DYNAMIC-LIBRARY,$(1), ,) && \ + $(call env_perform, \ + $(_LD_) --shared $(_LD_FLAGS_) $${dynamic_library_options} \ + -o $(1) $(2)) +endef + +# +# create an executable file from object file and/or library files +# +# $(1): executable file name +# $(2): objects files and/or libraries +# $(3): layout file +# $(4): options +# + +define env_executable + executable_options="" && \ + if [ -n "$(3)" ] ; then \ + executable_options="$${executable_options} -T $(3)" ; \ + fi && \ + if [ -n "$(4)" ] ; then \ + if [ $$(( $(4) & $(ENV_OPTION_NO_STANDARD) )) -ne 0 ] ; then \ + executable_options="$${executable_options} -nostdinc -nostdlib" ; \ + fi ; \ + fi && \ + $(call env_display,magenta,EXECUTABLE,$(1), ,) && \ + $(call env_perform, \ + $(_CC_) $(_CC_FLAGS_) $(_LD_FLAGS_) $${executable_options} -o $(1) \ + $(2) "`$(_CC_) -print-libgcc-file-name`") +endef + +# +# create an archive file from multiple object files +# +# note that the archive file is also an object file +# +# $(1): archive file name +# $(2): objects files and/or libraries +# $(3): options +# + +define env_archive + archive_options="" && \ + $(call env_display,magenta,ARCHIVE,$(1), ,) && \ + $(call env_perform, \ + $(_LD_) -r $(_LD_FLAGS_) $${archive_options} -o $(1) $(2)) +endef + +# +# remove the files +# +# $(1): files +# $(2): options +# + +define env_remove + remove_options="" && \ + for f in $(1) ; do \ + if [ -e $${f} ] ; then \ + $(call env_display,red,REMOVE,$${f}, ,) ; \ + fi && \ + $(call env_perform, \ + $(_RM_) $${remove_options} $${f}) ; \ + done +endef + +# +# purge i.e clean the directory from unwanted files +# + +define env_purge + $(call env_display,red,PURGE,,,) && \ + $(call env_perform, \ + $(_PURGE_)) +endef + +# +# generate prototypes from a source file +# +# $(1): file list +# $(2): options +# + +define env_prototypes + prototypes_options="" && \ + for f in $(1) ; do \ + if [ -e $${f} ] ; then \ + $(call env_display,yellow,PROTOTYPES,$${f}, ,) && \ + $(call env_launch,$(_MKP_TOOL_), \ + $${prototypes_options} $${f},) ; \ + fi ; \ + done +endef + +# +# genereate header dependencies +# +# $(1): the files for which the dependencies are generated +# $(2): options +# + +define env_headers + headers_options="" && \ + for f in $(1) ; do \ + if [ -e $${f} ] ; then \ + $(call env_display,yellow,HEADERS,$$f, ,) && \ + $(call env_perform, \ + $(_CC_) $(_CC_FLAGS_) -M -MG $${headers_options} \ + $${f} >> $(_DEPENDENCY_MK_)) ; \ + fi ; \ + done +endef + +# +# generate a version file +# +# $(1): the version file to generate +# + +define env_version + $(call env_display,yellow,VERSION,$(1), ,) && \ + $(_ECHO_) -n "" > $(1) && \ + $(_ECHO_) -n "const char version[] = \"$(_TITLE_)-$(_VERSION_)" >> $(1) && \ + $(_ECHO_) " "$(shell $(_DATE_))" $(USER)@$(HOSTNAME)\";" >> $(1) +endef + +# +# create a link between two files +# +# $(1): link created +# $(2): destination +# $(3): options +# + +define env_link + link_options="" && \ + $(call env_display,cyan,LINK,$(1), ,) && \ + $(call env_perform, \ + $(_LN_) $${link_options} $(2) $(1)) +endef + +# +# compile a tex document +# +# $(1): the file name without extension +# $(2): options +# + +define env_compile-tex + compile_tex_options="" && \ + $(call env_display,green,COMPILE-TEX,$(1), ,) && \ + $(call env_perform, \ + $(_PDFLATEX_) $${compile_tex_options} $(1)) && \ + $(call env_perform, \ + $(_PDFLATEX_) $${compile_tex_options} $(1)) && \ + $(call env_perform, \ + $(_BIBTEX_) $(1)) && \ + $(call env_perform, \ + $(_BIBTEX_) $(1)) && \ + $(call env_perform, \ + $(_PDFLATEX_) $${compile_tex_options} $(1)) ; \ + $(call env_perform, \ + $(_PDFLATEX_) $${compile_tex_options} $(1)) +endef + +# +# build a document +# +# $(1): the file name without extension +# $(2): options +# + +define env_document + $(call env_remove,$(_DEPENDENCY_TEX_),) && \ + if [ -n "$(2)" ] ; then \ + if [ $$(( $(2) & $(ENV_OPTION_PRIVATE) )) -ne 0 ] ; then \ + $(_ECHO_) '\def\mode{private}' > $(_DEPENDENCY_TEX_) ; \ + fi ; \ + fi && \ + if [ ! -f $(_DEPENDENCY_TEX_) ] ; then \ + $(_ECHO_) '\def\mode{public}' > $(_DEPENDENCY_TEX_) ; \ + fi && \ + $(call env_compile-tex,$(1),$(2)) +endef + +# +# launch a document viewer +# +# $(1): the file name without extension +# $(2): options +# + +define env_view + $(call env_display,yellow,VIEW,$(1), ,) && \ + $(call env_perform, \ + $(_XPDF_) $(1).pdf) +endef + +# +# ---------- component-based behaviour ---------------------------------------- +# + +# +# kaneton +# + +ifneq ($(call findstring,kaneton,$(components)),) + _CC_FLAGS_ += -D___kaneton$$\kernel +endif diff --git a/environment/profile/host/linux/linux.py b/environment/profile/host/linux/linux.py new file mode 100644 index 0000000..4671720 --- /dev/null +++ b/environment/profile/host/linux/linux.py @@ -0,0 +1,239 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/environment/profile/host/linux/linux.py +# +# created julien quintard [tue may 8 13:20:21 2007] +# updated julien quintard [sat mar 5 09:13:35 2011] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file implements the remaining functions of the kaneton python interface. +# +# note that the host profile already provides many functions. these +# functions can be overriden but you will probably just use them. +# +# in addition, the host profile already imports some packages. +# + +# +# ---------- lang ------------------------------------------------------------- +# +# incorrect locales may prevent the system from compiling properly. +# +# the following forces the LANG environment variable to US. +# +os.putenv("LANG", "US") + +# +# ---------- python path ------------------------------------------------------ +# + +_pythonpath_ = os.getenv("PYTHONPATH") + +if not _pythonpath_: + os.putenv("PYTHONPATH", _PYTHON_INCLUDE_DIR_) +else: + os.putenv("PYTHONPATH", _pythonpath_ + ":" + _PYTHON_INCLUDE_DIR_) + +# +# ---------- functions -------------------------------------------------------- +# + +# +# colorize() +# +# this function returns a colorized text if the environment is configured +# to or simply the original text. +# +# note that this function implementation is based on UNIX escape sequences. +# +def colorize(text, color, options): + if _DISPLAY_ == _DISPLAY_UNCOLORED_: + return text + + if options & OPTION_FLICKERING: + text = "" + text + if options & OPTION_BOLD: + text = "" + text + + if color == COLOR_BLACK: + text = "" + text + elif color == COLOR_RED: + text = "" + text + elif color == COLOR_GREEN: + text = "" + text + elif color == COLOR_YELLOW: + text = "" + text + elif color == COLOR_BLUE: + text = "" + text + elif color == COLOR_MAGENTA: + text = "" + text + elif color == COLOR_CYAN: + text = "" + text + elif color == COLOR_WHITE: + text = "" + text + + return text + "" + + + +# +# launch() +# +# this function launch a new program/script/make etc. +# +def launch(file, arguments, options): + directory = None + info = None + status = 0 + wd = None + + if options & OPTION_QUIET: + output = " >/dev/null 2>&1" + else: + output = "" + + info = os.path.split(file) + + directory = info[0] + file = info[1] + + if directory: + wd = cwd(OPTION_NONE) + cd(directory, OPTION_NONE) + + if re.match("^.*\.sh$", file): + status = os.system(_SHELL_ + " " + file + " " + arguments + output) + elif re.match("^.*\.py$", file): + status = os.system(_PYTHON_ + " " + file + " " + arguments + output) + elif re.match("^.*\.pl$", file): + status = os.system(_PERL_ + " " + file + " " + arguments + output) + elif re.match("^Makefile$", file): + status = os.system(_MAKE_ + " -f " + file + " " + arguments + output) + else: + if directory: + file = "./" + file + + status = os.system(file + " " + arguments + output) + + if directory: + cd(wd, OPTION_NONE) + + return status + + + +# +# pack() +# +# this function creates an archive of the given directory. +# +def pack(directory, file, options): + launch(_TAR_, "-cjf " + file + " " + directory, OPTION_NONE) + + + +# +# unpack() +# +# this function unpackages an archive into the given (optional) directory. +# +def unpack(file, directory, options): + if directory: + launch(_TAR_, "-xjf " + file + " -C " + directory, OPTION_NONE) + else: + launch(_TAR_, "-xjf " + file, OPTION_NONE) + + + +# +# load() +# +# this function copies a file on a device, this device can be virtual: +# an image. +# +def load(file, device, path, options): + if options == OPTION_DEVICE: + return launch(_MCOPY_, "-o -n " + file + " " + device + path, + OPTION_NONE) + elif options == OPTION_IMAGE: + return launch(_MCOPY_, "-o -n " + "-i" + device + " " + + file + " ::" + path, OPTION_NONE) + + + +# +# record() +# +# this function runs the program recording a session. +# +def record(transcript, options): + directory = None + time = None + log = None + tmp = None + wd = None + + tmp = temporary(OPTION_DIRECTORY) + + directory = tmp + "/" + "transcript" + + log = directory + "/" + "log" + time = directory + "/" + "time" + + mkdir(directory, OPTION_NONE) + + launch(_SCRIPT_, "-q -t " + log + " -c " + + _TRANSCRIPT_CMD_ + " 2> " + time, OPTION_NONE) + + wd = cwd(OPTION_NONE) + + cd(tmp, OPTION_NONE) + + pack("transcript", wd + "/" + transcript, OPTION_NONE) + + cd(wd, OPTION_NONE) + + remove(tmp, OPTION_NONE) + + + +# +# play() +# +# this function runs the program replaying a session. +# +def play(transcript, options): + directory = None + time = None + log = None + tmp = None + wd = None + + tmp = temporary(OPTION_DIRECTORY) + + log = tmp + "/" + "transcript/log" + time = tmp + "/" + "transcript/time" + + unpack(transcript, tmp, OPTION_NONE) + + launch(_REPLAY_TOOL_, time + " " + log, OPTION_NONE) + + remove(tmp, OPTION_NONE) + + + +# +# locate() +# +# this function tries to locate a program on the system. +# +def locate(file, options): + return launch(_WHICH_, file + " 1>/dev/null 2>/dev/null", OPTION_NONE) diff --git a/environment/profile/kaneton/core/core.conf b/environment/profile/kaneton/core/core.conf new file mode 100644 index 0000000..47050bb --- /dev/null +++ b/environment/profile/kaneton/core/core.conf @@ -0,0 +1,152 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kane...vironment/profile/kaneton/core/core.conf +# +# created julien quintard [tue may 8 13:42:57 2007] +# updated julien quintard [sun nov 28 19:36:43 2010] +# + +# +# ---------- information ------------------------------------------------------ +# +# these variables are core-dependent are used to parameterise the kernel. +# + +# +# ---------- flags ------------------------------------------------------------ +# + +_CORE_FLAGS_ = ${_AS_FLAGS_} \ + ${_CAPABILITY_FLAGS_} \ + ${_CPU_FLAGS_} \ + ${_EVENT_FLAGS_} \ + ${_ID_FLAGS_} \ + ${_INTERFACE_FLAGS_} \ + ${_IO_FLAGS_} \ + ${_KERNEL_FLAGS_} \ + ${_MAP_FLAGS_} \ + ${_MESSAGE_FLAGS_} \ + ${_REGION_FLAGS_} \ + ${_SCHEDULER_FLAGS_} \ + ${_SEGMENT_FLAGS_} \ + ${_SET_FLAGS_} \ + ${_TASK_FLAGS_} \ + ${_THREAD_FLAGS_} \ + ${_TIME_FLAGS_} + +# +# ---------- as manager ------------------------------------------------------- +# + +_AS_FLAGS_ = + +# +# ---------- capability manager ----------------------------------------------- +# + +_CAPABILITY_FLAGS_ = + +# +# ---------- cpu manager ------------------------------------------------------ +# + +_CPU_FLAGS_ = + +# +# ---------- event manager ---------------------------------------------------- +# + +_EVENT_FLAGS_ = + +# +# ---------- id manager ------------------------------------------------------- +# + +_ID_FLAGS_ = + +# +# ---------- interface manager ------------------------------------------------ +# + +_INTERFACE_FLAGS_ = + +# +# ---------- io manager ------------------------------------------------------- +# + +_IO_FLAGS_ = + +# +# ---------- kernel manager --------------------------------------------------- +# + +_KERNEL_FLAGS_ = + +# +# ---------- map manager ------------------------------------------------------ +# + +_MAP_FLAGS_ = + +# +# ---------- message manager -------------------------------------------------- +# + +_MESSAGE_FLAGS_ = + +# +# ---------- region manager --------------------------------------------------- +# + +_REGION_FLAGS_ = ${_REGION_ALGORITHM_} \ + ${_REGION_FIT_} + +_REGION_ALGORITHM_ = -DREGION_ALGORITHM=REGION_ALGORITHM_FIT +_REGION_FIT_ = -DREGION_FIT=FIT_FIRST + +# +# ---------- scheduler manager ------------------------------------------------ +# + +_SCHEDULER_FLAGS_ = ${_SCHEDULER_ALGORITHM_} + +_SCHEDULER_ALGORITHM_ = -DSCHEDULER_ALGORITHM=SCHEDULER_ALGORITHM_MFQ + +# +# ---------- segment manager -------------------------------------------------- +# + +_SEGMENT_FLAGS_ = ${_SEGMENT_ALGORITHM_} \ + ${_SEGMENT_FIT_} + +_SEGMENT_ALGORITHM_ = -DSEGMENT_ALGORITHM=SEGMENT_ALGORITHM_FIT +_SEGMENT_FIT_ = -DSEGMENT_FIT=FIT_FIRST + +# +# ---------- set manager ------------------------------------------------------ +# + +_SET_FLAGS_ = + +# +# ---------- task manager ----------------------------------------------------- +# + +_TASK_FLAGS_ = + +# +# ---------- thread manager --------------------------------------------------- +# + +_THREAD_FLAGS_ = + +# +# ---------- time manager ----------------------------------------------------- +# + +_TIME_FLAGS_ = diff --git a/environment/profile/kaneton/core/core.desc b/environment/profile/kaneton/core/core.desc new file mode 100644 index 0000000..6bb7d70 --- /dev/null +++ b/environment/profile/kaneton/core/core.desc @@ -0,0 +1,26 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kane...vironment/profile/kaneton/core/core.desc +# +# created julien quintard [tue may 8 13:43:36 2007] +# updated julien quintard [thu may 31 21:49:29 2007] +# + +# +# ---------- information ------------------------------------------------------ +# +# this files contains the core variable descriptions. +# + +- variable: _SEGMENT_LOOKUP_ALGORITHM_ + string: Segment Manager Algorithm + type: set + values: + First Fit: -DSEGMENT_LOOKUP_ALGORITHM=FIT_FIRST + Best Fit: -DSEGMENT_LOOKUP_ALGORITHM=FIT_BEST + description: Specify the type of algorithm to use. diff --git a/environment/profile/kaneton/kaneton.conf b/environment/profile/kaneton/kaneton.conf new file mode 100644 index 0000000..1f9c9df --- /dev/null +++ b/environment/profile/kaneton/kaneton.conf @@ -0,0 +1,34 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kane...environment/profile/kaneton/kaneton.conf +# +# created julien quintard [tue may 8 13:26:13 2007] +# updated julien quintard [sat feb 5 12:17:24 2011] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file contains general kaneton microkernel configuration variables. +# + +# +# ---------- general ---------------------------------------------------------- +# + +_TITLE_ = kaneton +_VERSION_ = 0.7 + +# +# ---------- flags ------------------------------------------------------------ +# + +_KANETON_FLAGS_ = ${_CORE_FLAGS_} \ + ${_MACHINE_FLAGS_} \ + ${_LIBRARY_FLAGS_} \ + ${_MODULES_FLAGS_} diff --git a/environment/profile/kaneton/kaneton.desc b/environment/profile/kaneton/kaneton.desc new file mode 100644 index 0000000..4c9f2b2 --- /dev/null +++ b/environment/profile/kaneton/kaneton.desc @@ -0,0 +1,12 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/environment/profile/kaneton/kaneton.desc +# +# created julien quintard [tue may 8 13:42:18 2007] +# updated julien quintard [tue may 8 13:42:18 2007] +# diff --git a/environment/profile/kaneton/library/library.conf b/environment/profile/kaneton/library/library.conf new file mode 100644 index 0000000..492bce7 --- /dev/null +++ b/environment/profile/kaneton/library/library.conf @@ -0,0 +1,24 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kane...ent/profile/kaneton/library/library.conf +# +# created julien quintard [fri may 1 20:05:33 2009] +# updated julien quintard [fri may 1 20:05:55 2009] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file contains the configuration of the in-kernel library. +# + +# +# ---------- flags ------------------------------------------------------------ +# + +_LIBRARY_FLAGS_ = diff --git a/environment/profile/kaneton/library/library.desc b/environment/profile/kaneton/library/library.desc new file mode 100644 index 0000000..830b771 --- /dev/null +++ b/environment/profile/kaneton/library/library.desc @@ -0,0 +1,12 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kane...ent/profile/kaneton/library/library.desc +# +# created julien quintard [fri may 1 20:06:04 2009] +# updated julien quintard [fri may 1 20:06:05 2009] +# diff --git a/environment/profile/kaneton/machine/machine.conf b/environment/profile/kaneton/machine/machine.conf new file mode 100644 index 0000000..1bd035c --- /dev/null +++ b/environment/profile/kaneton/machine/machine.conf @@ -0,0 +1,24 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kane...ent/profile/kaneton/machine/machine.conf +# +# created julien quintard [fri may 1 19:37:15 2009] +# updated julien quintard [fri may 1 19:37:48 2009] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file contains the machine configuration such as flags etc. +# + +# +# ---------- flags ------------------------------------------------------------ +# + +_MACHINE_FLAGS_ = diff --git a/environment/profile/kaneton/machine/machine.desc b/environment/profile/kaneton/machine/machine.desc new file mode 100644 index 0000000..e8b7187 --- /dev/null +++ b/environment/profile/kaneton/machine/machine.desc @@ -0,0 +1,12 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kane...ent/profile/kaneton/machine/machine.desc +# +# created julien quintard [fri may 1 19:38:08 2009] +# updated julien quintard [fri may 1 19:38:08 2009] +# diff --git a/environment/profile/kaneton/machine/platform/qemu-mips/qemu-mips.conf b/environment/profile/kaneton/machine/platform/qemu-mips/qemu-mips.conf new file mode 100755 index 0000000..dfaafe8 --- /dev/null +++ b/environment/profile/kaneton/machine/platform/qemu-mips/qemu-mips.conf @@ -0,0 +1,12 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/enguerrand/...achine/platform/qemu-mips/qemu-mips.conf +# +# created enguerrand raymond [sat apr 11 01:29:35 2009] +# updated enguerrand raymond [tue may 12 00:48:19 2009] +# diff --git a/environment/profile/kaneton/machine/platform/qemu-mips/qemu-mips.desc b/environment/profile/kaneton/machine/platform/qemu-mips/qemu-mips.desc new file mode 100755 index 0000000..e69de29 diff --git a/environment/profile/kaneton/modules/modules.conf b/environment/profile/kaneton/modules/modules.conf new file mode 100644 index 0000000..e06ff35 --- /dev/null +++ b/environment/profile/kaneton/modules/modules.conf @@ -0,0 +1,64 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kane...ent/profile/kaneton/modules/modules.conf +# +# created julien quintard [fri may 1 19:28:05 2009] +# updated julien quintard [sun dec 5 00:59:12 2010] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file contains information related to kaneton static modules. +# + +# +# ---------- modules ---------------------------------------------------------- +# + +_MODULES_ = console report + +# +# ---------- flags ------------------------------------------------------------ +# + +_MODULES_FLAGS_ = ${_MODULE_CONSOLE_FLAGS_} \ + ${_MODULE_FORWARD_FLAGS_} \ + ${_MODULE_TEST_FLAGS_} \ + ${_MODULE_BUNDLE_FLAGS_} \ + ${_MODULE_REPORT_FLAGS_} + +# +# ---------- console ---------------------------------------------------------- +# + +_MODULE_CONSOLE_FLAGS_ = + +# +# ---------- forward ---------------------------------------------------------- +# + +_MODULE_FORWARD_FLAGS_ = + +# +# ---------- test ------------------------------------------------------------- +# + +_MODULE_TEST_FLAGS_ = + +# +# ---------- bundle ----------------------------------------------------------- +# + +_MODULE_BUNDLE_FLAGS_ = + +# +# ---------- report ----------------------------------------------------------- +# + +_MODULE_REPORT_FLAGS_ = diff --git a/environment/profile/kaneton/modules/modules.desc b/environment/profile/kaneton/modules/modules.desc new file mode 100644 index 0000000..c4ee58d --- /dev/null +++ b/environment/profile/kaneton/modules/modules.desc @@ -0,0 +1,12 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kane...ent/profile/kaneton/modules/modules.desc +# +# created julien quintard [fri may 1 19:36:15 2009] +# updated julien quintard [fri may 1 19:36:15 2009] +# diff --git a/environment/profile/user/sample/sample.conf b/environment/profile/user/sample/sample.conf new file mode 100644 index 0000000..2e2da9b --- /dev/null +++ b/environment/profile/user/sample/sample.conf @@ -0,0 +1,39 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kane...ironment/profile/user/sample/sample.conf +# +# created julien quintard [sat jun 9 22:40:33 2007] +# updated julien quintard [thu mar 3 18:18:10 2011] +# + +# +# ---------- information ------------------------------------------------------ +# +# a sample of the user configuration file. +# + +# +# ---------- boot stuff ------------------------------------------------------- +# + +_MBL_SCRIPT_ = ${_MBL_DIR_}/grub/grub.py + +_BOOT_MODE_ = image +_BOOT_DEVICE_ = floppy + +# +# ---------- environment ------------------------------------------------------ +# + +_OUTPUT_ = ${_OUTPUT_VERBOSE_} + +# +# ---------- inputs ----------------------------------------------------------- +# + +#_INPUTS_ += ${_SAMPLE_DIR_}/chiche/chiche diff --git a/environment/profile/user/test/test.conf b/environment/profile/user/test/test.conf new file mode 100644 index 0000000..e24fb61 --- /dev/null +++ b/environment/profile/user/test/test.conf @@ -0,0 +1,33 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/environment/profile/user/test/test.conf +# +# created julien quintard [thu apr 16 04:29:52 2009] +# updated julien quintard [sat dec 18 21:24:24 2010] +# + +# +# ---------- boot ------------------------------------------------------------- +# + +_MBL_SCRIPT_ = ${_MBL_DIR_}/grub/grub.py + +_BOOT_MODE_ = image +_BOOT_DEVICE_ = floppy + +# +# ---------- modules ---------------------------------------------------------- +# + +_MODULES_ += bundle test + +# +# ---------- display ---------------------------------------------------------- +# + +_DISPLAY_ = ${_DISPLAY_UNCOLORED_} diff --git a/environment/profile/user/user.conf b/environment/profile/user/user.conf new file mode 100644 index 0000000..11c927f --- /dev/null +++ b/environment/profile/user/user.conf @@ -0,0 +1,65 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/environment/profile/user/user.conf +# +# created julien quintard [tue may 8 10:47:42 2007] +# updated julien quintard [sat may 7 14:13:14 2011] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file defines the default user profile environment variables. +# + +# +# ---------- display ---------------------------------------------------------- +# + +_DISPLAY_UNCOLORED_ = 1 +_DISPLAY_COLORED_ = 2 + +_DISPLAY_ = ${_DISPLAY_COLORED_} + +# +# ---------- output ----------------------------------------------------------- +# + +_OUTPUT_PRETTY_ = 1 +_OUTPUT_VERBOSE_ = 2 + +_OUTPUT_ = ${_OUTPUT_PRETTY_} + +# +# ---------- cheat ------------------------------------------------------------ +# + +_CHEAT_FILTER_ = configure \ + environment \ + license \ + tool \ + transcript + +# +# ---------- transcript ------------------------------------------------------- +# + +_TRANSCRIPT_CMD_ = ${_SHELL_} + +# +# ---------- test ------------------------------------------------------------- +# + +_TEST_SERVER_ = https://test.opaak.org:8421 + +_TEST_CAPABILITY_ = ${_PROFILE_USER_DIR_}/${_USER_}.cap +_TEST_PLATFORM_ = ${_PLATFORM_} +_TEST_ARCHITECTURE_ = ${_ARCHITECTURE_} + +_TEST_NAME_ = +_TEST_EMAIL_ = diff --git a/environment/profile/user/user.desc b/environment/profile/user/user.desc new file mode 100644 index 0000000..13246e6 --- /dev/null +++ b/environment/profile/user/user.desc @@ -0,0 +1,26 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/environment/profile/user/user.desc +# +# created julien quintard [fri may 25 09:36:30 2007] +# updated julien quintard [thu may 31 21:49:03 2007] +# + +# +# ---------- information ------------------------------------------------------ +# +# this file contains the description of the user variables. +# + +- variable: _DISPLAY_ + string: Display Colorization + type: set + values: + Uncolored: ${_DISPLAY_UNCOLORED_} + Colored: ${_DISPLAY_COLORED_} + description: Activate or not the colorization of the environment messages. diff --git a/export/Makefile b/export/Makefile new file mode 100644 index 0000000..4635cdc --- /dev/null +++ b/export/Makefile @@ -0,0 +1,53 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton.STABLE/export/Makefile +# +# created julien quintard [sun jun 10 14:53:00 2007] +# updated julien quintard [tue nov 23 22:28:16 2010] +# + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main export- export clear prototypes headers + +# +# ---------- variables -------------------------------------------------------- +# + +SUBDIRS := modules + +# +# ---------- rules ------------------------------------------------------------ +# + +main: + +export- export: + $(call env_launch,$(_EXPORT_SCRIPT_),$*,) + +export-%: + $(call env_launch,$(_EXPORT_SCRIPT_),$*,) + +clear: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,clear,) ; \ + done + + $(call env_purge) + +prototypes: + +headers: diff --git a/export/behaviours/sets/chop.yml b/export/behaviours/sets/chop.yml new file mode 100644 index 0000000..6b82590 --- /dev/null +++ b/export/behaviours/sets/chop.yml @@ -0,0 +1,146 @@ +# +# ---------- information ------------------------------------------------------ +# +# this set of operation removes all the unecessary components such as the +# cheat/, test/, history/ etc. directories. +# +# note that the root Makefile is also replaced in order to consier the +# new environment. +# + +# +# directories +# +- + operation: fremove + path: TODO + +- + operation: fremove + path: README + +- + operation: fremove + path: cheat + +- + operation: fremove + path: check + +- + operation: fremove + path: configure + +- + operation: fremove + path: history + +- + operation: fremove + path: transcript + +- + operation: fremove + path: view + +- + operation: freplace + src: export/data/snapshot/Makefile + dst: Makefile + +# +# test +# + +- + operation: fremove + path: test/configuration + +- + operation: fremove + path: test/engine + +- + operation: fremove + path: test/environments + +- + operation: fremove + path: test/hooks + +- + operation: fremove + path: test/images + +- + operation: fremove + path: test/robot + +- + operation: fremove + path: test/scripts + +- + operation: fremove + path: test/server + +- + operation: fremove + path: test/stages + +- + operation: fremove + path: test/store + +- + operation: fremove + path: test/suites + +- + operation: fremove + path: test/tests + +- + operation: fremove + path: test/utilities + +- + operation: freplace + src: export/data/snapshot/test/Makefile + dst: test/Makefile + +# +# tools +# +- + operation: fremove + path: tool/ctc + +- + operation: fremove + path: tool/firmware + +- + operation: fremove + path: tool/forest + +- + operation: fremove + path: tool/interface + +- + operation: fremove + path: tool/mbl/octaneload + +- + operation: fremove + path: tool/mbl/qemu-mips + +- + operation: fremove + path: tool/script + +- + operation: freplace + src: export/data/snapshot/tool/Makefile + dst: tool/Makefile diff --git a/export/behaviours/sets/export.yml b/export/behaviours/sets/export.yml new file mode 100644 index 0000000..873d9b7 --- /dev/null +++ b/export/behaviours/sets/export.yml @@ -0,0 +1,23 @@ +# +# ---------- information ------------------------------------------------------ +# +# this set of operation removes all the exported kaneton implementations. +# + +- + operation: fremove + path: export/output/snapshot.tar.bz2 + +- + operation: fremove + path: export/output/test:contributor.tar.bz2 + +- + operation: fremove + path: export/output/test:robot.tar.bz2 + +- + operation: fremove + path: export/output/test:student.tar.bz2 + + diff --git a/export/behaviours/sets/ibm-pc.ia32.yml b/export/behaviours/sets/ibm-pc.ia32.yml new file mode 100644 index 0000000..7ad56c2 --- /dev/null +++ b/export/behaviours/sets/ibm-pc.ia32.yml @@ -0,0 +1,68 @@ +# +# ---------- information ------------------------------------------------------ +# +# this set of operation removes every profile so that only the ibm-pc.ia32 +# machine remains. +# + +# +# architectures/platforms +# +- + operation: fremove + path: boot/loader/null.null + +- + operation: fremove + path: boot/loader/qemu-mips.mips64 + +- + operation: fremove + path: kaneton/machine/architecture/mips64 + +- + operation: fremove + path: kaneton/machine/architecture/null + +- + operation: fremove + path: kaneton/machine/architecture/ultrasparc + +- + operation: fremove + path: kaneton/machine/glue/null.null + +- + operation: fremove + path: kaneton/machine/glue/octane.mips64 + +- + operation: fremove + path: kaneton/machine/glue/qemu-mips.mips64 + +- + operation: fremove + path: kaneton/machine/platform/null + +- + operation: fremove + path: kaneton/machine/platform/octane + +- + operation: fremove + path: kaneton/machine/platform/qemu-mips + +- + operation: fremove + path: kaneton/machine/platform/sun4u + +# +# optimised +# +- + operation: fremove + path: kaneton/machine/glue/ibm-pc.ia32/optimised + +- + operation: fremove + path: kaneton/machine/architecture/ia32/optimised diff --git a/export/behaviours/sets/k0.yml b/export/behaviours/sets/k0.yml new file mode 100644 index 0000000..82c4b9a --- /dev/null +++ b/export/behaviours/sets/k0.yml @@ -0,0 +1,31 @@ +# +# ---------- information ------------------------------------------------------ +# +# this set of operations removes parts of the source code in order for +# students to undertake the k0 project. +# + +# +# boot +# +- + operation: fremove + path: boot/strap + +- + operation: fremove + path: boot/loader/ibm-pc.ia32 + +- + operation: mkdir + path: boot/loader/ibm-pc.ia32/educational + +- + operation: freplace + src: export/data/snapshot/boot/loader/ibm-pc.ia32/educational/Makefile + dst: boot/loader/ibm-pc.ia32/educational/Makefile + +- + operation: freplace + src: export/data/snapshot/boot/loader/ibm-pc.ia32/educational/loader + dst: boot/loader/ibm-pc.ia32/educational/loader diff --git a/export/behaviours/sets/k1.yml b/export/behaviours/sets/k1.yml new file mode 100644 index 0000000..f66f7c2 --- /dev/null +++ b/export/behaviours/sets/k1.yml @@ -0,0 +1,52 @@ +# +# ---------- information ------------------------------------------------------ +# +# this set of operations removes parts of the source code in order for +# students to undertake the k1 project. +# + +# +# glue +# +- + operation: freplace + src: export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/event.c + dst: kaneton/machine/glue/ibm-pc.ia32/educational/event.c + +- + operation: freplace + src: export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/include/event.h + dst: kaneton/machine/glue/ibm-pc.ia32/educational/include/event.h + +# +# architecture +# +- + operation: freplace + src: export/data/snapshot/kaneton/machine/architecture/ia32/educational/handler.c + dst: kaneton/machine/architecture/ia32/educational/handler.c + +- + operation: freplace + src: export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/handler.h + dst: kaneton/machine/architecture/ia32/educational/include/handler.h + +- + operation: freplace + src: export/data/snapshot/kaneton/machine/architecture/ia32/educational/idt.c + dst: kaneton/machine/architecture/ia32/educational/idt.c + +- + operation: freplace + src: export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/idt.h + dst: kaneton/machine/architecture/ia32/educational/include/idt.h + +- + operation: freplace + src: export/data/snapshot/kaneton/machine/architecture/ia32/educational/environment.c + dst: kaneton/machine/architecture/ia32/educational/environment.c + +- + operation: freplace + src: export/data/snapshot/kaneton/machine/architecture/ia32/educational/context.c + dst: kaneton/machine/architecture/ia32/educational/context.c diff --git a/export/behaviours/sets/k2.yml b/export/behaviours/sets/k2.yml new file mode 100644 index 0000000..730ee40 --- /dev/null +++ b/export/behaviours/sets/k2.yml @@ -0,0 +1,62 @@ +# +# ---------- information ------------------------------------------------------ +# +# this set of operations removes parts of the source code in order for +# students to undertake the k2 project. +# + +# +# glue +# +- + operation: freplace + src: export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/as.c + dst: kaneton/machine/glue/ibm-pc.ia32/educational/as.c + +- + operation: freplace + src: export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/include/as.h + dst: kaneton/machine/glue/ibm-pc.ia32/educational/include/as.h + +- + operation: freplace + src: export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/region.c + dst: kaneton/machine/glue/ibm-pc.ia32/educational/region.c + +# +# architecture +# +- + operation: freplace + src: export/data/snapshot/kaneton/machine/architecture/ia32/educational/environment.c + dst: kaneton/machine/architecture/ia32/educational/environment.c + +- + operation: freplace + src: export/data/snapshot/kaneton/machine/architecture/ia32/educational/pd.c + dst: kaneton/machine/architecture/ia32/educational/pd.c + +- + operation: freplace + src: export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/pd.h + dst: kaneton/machine/architecture/ia32/educational/include/pd.h + +- + operation: freplace + src: export/data/snapshot/kaneton/machine/architecture/ia32/educational/pt.c + dst: kaneton/machine/architecture/ia32/educational/pt.c + +- + operation: freplace + src: export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/pt.h + dst: kaneton/machine/architecture/ia32/educational/include/pt.h + +- + operation: freplace + src: export/data/snapshot/kaneton/machine/architecture/ia32/educational/paging.c + dst: kaneton/machine/architecture/ia32/educational/paging.c + +- + operation: freplace + src: export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/paging.h + dst: kaneton/machine/architecture/ia32/educational/include/paging.h diff --git a/export/behaviours/sets/k3.yml b/export/behaviours/sets/k3.yml new file mode 100644 index 0000000..880df11 --- /dev/null +++ b/export/behaviours/sets/k3.yml @@ -0,0 +1,40 @@ +# +# ---------- information ------------------------------------------------------ +# +# this set of operations removes parts of the source code in order for +# students to undertake the k3 project. +# + +# +# core +# +- + operation: freplace + src: export/data/snapshot/kaneton/core/scheduler/scheduler-mfq.c + dst: kaneton/core/scheduler/scheduler-mfq.c + +- + operation: freplace + src: export/data/snapshot/kaneton/core/include/scheduler.h + dst: kaneton/core/include/scheduler.h + +# +# glue +# +- + operation: freplace + src: export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/scheduler.c + dst: kaneton/machine/glue/ibm-pc.ia32/educational/scheduler.c + +# +# architecture +# +- + operation: freplace + src: export/data/snapshot/kaneton/machine/architecture/ia32/educational/context.c + dst: kaneton/machine/architecture/ia32/educational/context.c + +- + operation: freplace + src: export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/context.h + dst: kaneton/machine/architecture/ia32/educational/include/context.h diff --git a/export/behaviours/sets/k4.yml b/export/behaviours/sets/k4.yml new file mode 100644 index 0000000..aa3cb4f --- /dev/null +++ b/export/behaviours/sets/k4.yml @@ -0,0 +1,8 @@ +# +# ---------- information ------------------------------------------------------ +# +# this set of operations removes parts of the source code in order for +# students to undertake the k4 project. +# + +[] diff --git a/export/behaviours/sets/sweep.yml b/export/behaviours/sets/sweep.yml new file mode 100644 index 0000000..847b3b6 --- /dev/null +++ b/export/behaviours/sets/sweep.yml @@ -0,0 +1,33 @@ +# +# ---------- information ------------------------------------------------------ +# +# this set of operation removes all the versioning-system-specific information. +# + +# +# subversion +# +- + operation: fremovepattern + pattern: \.svn + +# +# cvs +# +- + operation: fremovepattern + pattern: CVS + +# +# git +# +- + operation: fremovepattern + pattern: \.git + +# +# mercurial +# +- + operation: fremovepattern + pattern: \.hg diff --git a/export/behaviours/sets/user:sample.yml b/export/behaviours/sets/user:sample.yml new file mode 100644 index 0000000..e13861d --- /dev/null +++ b/export/behaviours/sets/user:sample.yml @@ -0,0 +1,29 @@ +# +# ---------- information ------------------------------------------------------ +# +# this set of operation removes the unecessary users so that only 'sample' +# remains. +# + +# +# users +# +- + operation: fremove + path: environment/profile/user/enguerrand.raymond + +- + operation: fremove + path: environment/profile/user/francois.goudal + +- + operation: fremove + path: environment/profile/user/jean.guyader + +- + operation: fremove + path: environment/profile/user/julien.quintard + +- + operation: fremove + path: environment/profile/user/robot diff --git a/export/behaviours/sets/user:test.yml b/export/behaviours/sets/user:test.yml new file mode 100644 index 0000000..5d019d5 --- /dev/null +++ b/export/behaviours/sets/user:test.yml @@ -0,0 +1,29 @@ +# +# ---------- information ------------------------------------------------------ +# +# this set of operation removes the unecessary users so that only 'test' +# remains. +# + +# +# users +# +- + operation: fremove + path: environment/profile/user/enguerrand.raymond + +- + operation: fremove + path: environment/profile/user/francois.goudal + +- + operation: fremove + path: environment/profile/user/jean.guyader + +- + operation: fremove + path: environment/profile/user/julien.quintard + +- + operation: fremove + path: environment/profile/user/sample diff --git a/export/behaviours/snapshot.yml b/export/behaviours/snapshot.yml new file mode 100644 index 0000000..16ffe1e --- /dev/null +++ b/export/behaviours/snapshot.yml @@ -0,0 +1,55 @@ +# +# ---------- information ------------------------------------------------------ +# +# this behaviour is used in order to create a kaneton snapshot for students. +# + +- + operation: svnexport + +- + operation: import + filename: sets/sweep + +- + operation: import + filename: sets/k0 + +- + operation: import + filename: sets/k1 + +- + operation: import + filename: sets/k2 + +- + operation: import + filename: sets/k3 + +- + operation: import + filename: sets/k4 + +- + operation: import + filename: sets/chop + +- + operation: import + filename: sets/ibm-pc.ia32 + +- + operation: import + filename: sets/user:sample + +- + operation: import + filename: sets/export + +- + operation: initenv + +- + operation: tarball + filename: snapshot diff --git a/export/behaviours/test:contributor.yml b/export/behaviours/test:contributor.yml new file mode 100644 index 0000000..f942361 --- /dev/null +++ b/export/behaviours/test:contributor.yml @@ -0,0 +1,36 @@ +# +# ---------- information ------------------------------------------------------ +# +# this behaviour is used in order to create a kaneton snapshot contributors +# to invoke the test system. +# + +- + operation: localexport + +- + operation: initenv + +- + operation: import + filename: sets/sweep + +- + operation: import + filename: sets/chop + +- + operation: import + filename: sets/ibm-pc.ia32 + +- + operation: import + filename: sets/export + +- + operation: fremove + path: export/output/test:contributor.tar.bz2 + +- + operation: tarball + filename: test:contributor diff --git a/export/behaviours/test:group.yml b/export/behaviours/test:group.yml new file mode 100644 index 0000000..b963b08 --- /dev/null +++ b/export/behaviours/test:group.yml @@ -0,0 +1,40 @@ +# +# ---------- information ------------------------------------------------------ +# +# this behaviour is used in order to create a kaneton snapshot students +# to invoke the test system. +# + +- + operation: localexport + +- + operation: initenv + +- + operation: import + filename: sets/sweep + +- + operation: import + filename: sets/chop + +- + operation: import + filename: sets/ibm-pc.ia32 + +- + operation: import + filename: sets/user:test + +- + operation: import + filename: sets/export + +- + operation: fremove + path: export/output/test:group.tar.bz2 + +- + operation: tarball + filename: test:group diff --git a/export/behaviours/test:student.yml b/export/behaviours/test:student.yml new file mode 100644 index 0000000..30770e8 --- /dev/null +++ b/export/behaviours/test:student.yml @@ -0,0 +1,40 @@ +# +# ---------- information ------------------------------------------------------ +# +# this behaviour is used in order to create a kaneton snapshot students +# to invoke the test system. +# + +- + operation: localexport + +- + operation: initenv + +- + operation: import + filename: sets/sweep + +- + operation: import + filename: sets/chop + +- + operation: import + filename: sets/ibm-pc.ia32 + +- + operation: import + filename: sets/user:test + +- + operation: import + filename: sets/export + +- + operation: fremove + path: export/output/test:student.tar.bz2 + +- + operation: tarball + filename: test:student diff --git a/export/data/snapshot/Makefile b/export/data/snapshot/Makefile new file mode 100644 index 0000000..8fb8b5d --- /dev/null +++ b/export/data/snapshot/Makefile @@ -0,0 +1,145 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kanetno +# +# file /home/mycure/kaneton/export/data/snapshot/Makefile +# +# created julien quintard [tue jun 26 11:27:22 2007] +# updated julien quintard [sat feb 5 12:11:16 2011] +# + +# +# ---------- dependencies ----------------------------------------------------- +# + +-include environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.SILENT: + +.PHONY: main initialize clean clear prototypes \ + headers build install info + +# +# ---------- variables -------------------------------------------------------- +# + +_PYTHON_ ?= $(KANETON_PYTHON) +_MAKE_ ?= $(MAKE) + +# +# ---------- default rule ----------------------------------------------------- +# + +ifeq ($(_SIGNATURE_),kaneton) + +PATHS = $(dir $(_COMPONENTS_)) + + main: + for path in $(PATHS) ; do \ + if [ -f "$${path}/Makefile" ] ; then \ + $(call env_launch,$${path}/Makefile,,) ; \ + fi \ + done + +else + + main \ + clear \ + prototypes \ + headers \ + build install \ + info \ + clean: initialize + $(_MAKE_) -f Makefile $@ + +endif + +# +# ---------- environment ------------------------------------------------------ +# + +initialize: + cd environment/ && \ + $(_PYTHON_) initialize.py && \ + cd .. + +# +# ---------- conditional ------------------------------------------------------ +# + +ifeq ($(_SIGNATURE_),kaneton) + +# +# ---------- environment ------------------------------------------------------ +# + +clean: + $(call env_launch,$(_CLEAN_SCRIPT_),,) + +# +# ---------- variables -------------------------------------------------------- +# + +SUBDIRS := boot environment kaneton \ + license sample test tool \ + $(PATHS) + +# +# ---------- clear ------------------------------------------------------------ +# + +clear: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,clear,) ; \ + done + + $(call env_purge,) + +# +# ---------- prototypes ------------------------------------------------------- +# + +prototypes: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,prototypes,) ; \ + done + +# +# ---------- headers ---------------------------------------------------------- +# + +headers: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,headers,) ; \ + done + +# +# ---------- boot ------------------------------------------------------------- +# + +build: + $(call env_launch,$(_MBL_SCRIPT_),build,) + +install: main + $(call env_launch,$(_MBL_SCRIPT_),install,) + +# +# ---------- information ------------------------------------------------------ +# + +info: + $(call env_print,,,) + + $(call env_print,"--- ",blue,$(ENV_OPTION_NO_NEWLINE)) + $(call env_print,http://kaneton.opaak.org,,) + + $(call env_print,,,) + +endif diff --git a/export/data/snapshot/boot/loader/ibm-pc.ia32/educational/Makefile b/export/data/snapshot/boot/loader/ibm-pc.ia32/educational/Makefile new file mode 100644 index 0000000..2af0147 --- /dev/null +++ b/export/data/snapshot/boot/loader/ibm-pc.ia32/educational/Makefile @@ -0,0 +1,50 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kane.../loader/ibm-pc.ia32/educational/Makefile +# +# created julien quintard [tue jun 12 20:34:41 2007] +# updated julien quintard [sat feb 5 11:09:53 2011] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := loader + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: + +prototypes: + +headers: + +dependencies: + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/export/data/snapshot/boot/loader/ibm-pc.ia32/educational/loader b/export/data/snapshot/boot/loader/ibm-pc.ia32/educational/loader new file mode 100755 index 0000000000000000000000000000000000000000..32cbb9ea1fac404a182f8d7e52b594e7c47e07b9 GIT binary patch literal 20441 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 Dh5DHm literal 0 HcmV?d00001 diff --git a/export/data/snapshot/kaneton/core/include/scheduler.h b/export/data/snapshot/kaneton/core/include/scheduler.h new file mode 100644 index 0000000..0fcbdc6 --- /dev/null +++ b/export/data/snapshot/kaneton/core/include/scheduler.h @@ -0,0 +1,290 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...kaneton/kaneton/core/include/scheduler.h + * + * created julien quintard [wed jun 6 13:44:48 2007] + * updated julien quintard [sat feb 5 16:59:18 2011] + */ + +#ifndef CORE_SCHEDULER_H +#define CORE_SCHEDULER_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include +#include +#include + +#include + +/* + * ---------- algorithms ------------------------------------------------------ + */ + +/* + * the supported scheduler algorithms. + */ + +#define SCHEDULER_ALGORITHM_MFQ (1 << 0) + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * the scheduler state either started or stopped. + */ + +#define SCHEDULER_STATE_START 1 +#define SCHEDULER_STATE_STOP 2 + +/* + * initial value for the scheduler quantum in milliseconds. + */ + +#define SCHEDULER_QUANTUM TIMER_DELAY + +/* + * the number of priorities i.e the number of queues. + */ + +#define SCHEDULER_NPRIORITIES 60 + +/* + * timeslice bounds. + */ + +#define SCHEDULER_TIMESLICE_HIGH 250 +#define SCHEDULER_TIMESLICE_LOW 10 + +/* + * the timeslice granularity. + */ + +#define SCHEDULER_GRANULARITY _scheduler.quantum + +/* + * scheduling priorities. + */ + +#define SCHEDULER_PRIORITY_HIGH SCHEDULER_NPRIORITIES - 1 +#define SCHEDULER_PRIORITY_LOW 0 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function computes the thread's high precision character + * by taking into account both the task's and thread's priorities. + */ + +#define SCHEDULER_CHARACTER(_id_) \ + ( \ + { \ + o_task* _task_; \ + o_thread* _thread_; \ + \ + assert(thread_get((_id_), &_thread_) == ERROR_OK); \ + assert(task_get(_thread_->task, &_task_) == ERROR_OK); \ + \ + ((_task_->priority - TASK_PRIORITY_BACKGROUND_LOW) * \ + (_thread_->priority - THREAD_PRIORITY_LOW)); \ + } \ + ) + +/* + * this macro-function computes the priority for a giver thread. this + * is a low precision measurement of a thread's priority which is used + * for locating the proper scheduling queue. + * + * indeed, while the character lies in a large range, it is then + * reduced within the range [SCHEDULER_PRIORITY_LOW, SCHEDULER_PRIORITY_HIGH]. + */ + +#define SCHEDULER_PRIORITY(_thread_) \ + ( \ + { \ + t_priority _character_; \ + \ + _character_ = SCHEDULER_CHARACTER((_thread_)); \ + \ + SCHEDULER_PRIORITY_LOW + \ + ((_character_ * \ + (SCHEDULER_PRIORITY_HIGH - SCHEDULER_PRIORITY_LOW)) / \ + ((TASK_PRIORITY_KERNEL_HIGH - TASK_PRIORITY_BACKGROUND_LOW) * \ + (THREAD_PRIORITY_HIGH - THREAD_PRIORITY_LOW))); \ + } \ + ) + +/* + * this macro-function takes a number of milliseconds and turns it + * into a valid timeslice according to the scheduler quantum. + * + * for example, with a quantum of 25ms and a given number of 264 milliseconds, + * this macro-function would return 275ms, the upper rounded number. + */ + +#define SCHEDULER_SCALE(_timeslice_) \ + ((((_timeslice_) % SCHEDULER_GRANULARITY) != 0) ? \ + (((_timeslice_) + SCHEDULER_GRANULARITY) - \ + (_timeslice_) % SCHEDULER_GRANULARITY) \ + : (_timeslice_)) + +/* + * this macro-function computes the timeslice given by the kernel to a + * thread based on its character. + * + * the character basically returns task->priority * thread->priority. + * + * this number is then turned into a timeslice i.e within the timeslice + * range [SCHEDULER_TIMESLICE_LOW, SCHEDULER_TIMESLICE_HIGH]. + * + * finally, the timeslice is scaled i.e rounded up in order to fit the + * scheduling unit known as the quantum. + */ + +#define SCHEDULER_TIMESLICE(_thread_) \ + ( \ + { \ + t_priority _character_; \ + t_timeslice _timeslice_; \ + \ + _character_ = SCHEDULER_CHARACTER((_thread_)); \ + \ + _timeslice_ = \ + SCHEDULER_TIMESLICE_LOW + \ + ((_character_ * \ + (SCHEDULER_TIMESLICE_HIGH - SCHEDULER_TIMESLICE_LOW)) / \ + ((TASK_PRIORITY_KERNEL_HIGH - TASK_PRIORITY_BACKGROUND_LOW) * \ + (THREAD_PRIORITY_HIGH - THREAD_PRIORITY_LOW))); \ + \ + SCHEDULER_SCALE(_timeslice_); \ + } \ + ) + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * the scheduler object which managers thread candidates for a + * given CPU specified by _cpu_. + * + * the _thread_ attribute represents the currently scheduled thread + * which operates at the priority _priority_. this thread stil has + * _timeslice_ milliseconds of execution time before a context switch + * occurs. + * + * the _state_ attribute represents the scheduler's current state, either + * started or stopped. + */ + +typedef struct +{ + i_cpu cpu; + + i_thread thread; + t_timeslice timeslice; + t_priority priority; + + /* FIXME[code to complete] */ + + t_state state; + + machine_data(o_scheduler); +} o_scheduler; + +/* + * the scheduler manager's structure which contains the quantum _quantum_ + * i.e the smaller unit of execution time, the idle thread's identifier _idle_ + * and the sets of schedulers, one scheduler per CPU. + */ + +typedef struct +{ + t_quantum quantum; + + i_thread idle; + + i_set schedulers; + + machine_data(m_scheduler); +} m_scheduler; + +/* + * the scheduler dispatcher. + */ + +typedef struct +{ + t_error (*scheduler_show)(i_cpu, + mt_margin); + t_error (*scheduler_dump)(void); + t_error (*scheduler_start)(i_cpu); + t_error (*scheduler_stop)(i_cpu); + t_error (*scheduler_quantum)(t_quantum); + t_error (*scheduler_yield)(void); + t_error (*scheduler_elect)(void); + t_error (*scheduler_add)(i_thread); + t_error (*scheduler_remove)(i_thread); + t_error (*scheduler_update)(i_thread); + t_error (*scheduler_initialize)(void); + t_error (*scheduler_clean)(void); +} d_scheduler; + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../../core/scheduler/scheduler-mfq.c + */ + +/* + * ../../core/scheduler/scheduler-mfq.c + */ + +t_error scheduler_show(i_cpu id, + mt_margin margin); + +t_error scheduler_dump(void); + +t_error scheduler_start(i_cpu id); + +t_error scheduler_stop(i_cpu id); + +t_error scheduler_quantum(t_quantum quantum); + +t_error scheduler_yield(void); + +t_error scheduler_elect(void); + +t_error scheduler_add(i_thread id); + +t_error scheduler_remove(i_thread id); + +t_error scheduler_update(i_thread id); + +t_error scheduler_exist(i_cpu id); + +t_error scheduler_get(i_cpu id, + o_scheduler** object); + +t_error scheduler_current(o_scheduler** scheduler); + +t_error scheduler_initialize(void); + +t_error scheduler_clean(void); + + +/* + * eop + */ + +#endif diff --git a/export/data/snapshot/kaneton/core/scheduler/scheduler-mfq.c b/export/data/snapshot/kaneton/core/scheduler/scheduler-mfq.c new file mode 100644 index 0000000..b8fdf4e --- /dev/null +++ b/export/data/snapshot/kaneton/core/scheduler/scheduler-mfq.c @@ -0,0 +1,463 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/Down...n/kaneton/core/scheduler/scheduler-mfq.c + * + * created matthieu bucchianeri [sat jun 3 22:36:59 2006] + * updated julien quintard [mon apr 11 13:23:09 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * the scheduler manager provides functionalities for managing the + * execution of the tasks and their threads on the possibly multiple CPUs + * of the computer. + * + * note that tasks are not scheduled. indeed, the active entity is the + * thread. therefore, adding a thread to the scheduler makes it a candidate + * for future election. + * + * the core function is the scheduler_elect() function which chooses + * the next thread to execute, though the current thread may be selected + * to continue its execution. + */ + +#if (SCHEDULER_ALGORITHM == SCHEDULER_ALGORITHM_MFQ) + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * include the machine-specific definitions required by the core. + */ + +machine_include(scheduler); + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the kernel manager. + */ + +extern m_kernel _kernel; + +/* + * the cpu manager. + */ + +extern m_cpu _cpu; + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the scheduler manager. + */ + +m_scheduler _scheduler; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function starts the given scheduler. + * + * steps: + * + * 1) retrieve the scheduler object. + * 2) change the scheduler's state. + * 3) call the machine. + */ + +t_error scheduler_start(i_cpu id) +{ + o_scheduler* scheduler; + + /* + * 1) + */ + + if (scheduler_get(id, &scheduler) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the scheduler object"); + + /* + * 2) + */ + + scheduler->state = SCHEDULER_STATE_START; + + /* + * 3) + */ + + if (machine_call(scheduler, start, id) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function stops the given scheduler. + * + * steps: + * + * 1) retrieve the scheduler object. + * 2) change the scheduler's state. + * 3) call the machine. + */ + +t_error scheduler_stop(i_cpu id) +{ + o_scheduler* scheduler; + + /* + * 1) + */ + + if (scheduler_get(id, &scheduler) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the scheduler object"); + + /* + * 2) + */ + + scheduler->state = SCHEDULER_STATE_STOP; + + /* + * 3) + */ + + if (machine_call(scheduler, stop, id) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function modifies the quantum. + */ + +t_error scheduler_quantum(t_quantum quantum) +{ + /* FIXME[code to complete] */ + + CORE_LEAVE(); +} + +/* + * this function enables the current thread to voluntarily relinquish its + * execution, hence permitting another thread to be scheduled immediately + * on this CPU. + */ + +t_error scheduler_yield(void) +{ + /* FIXME[code to complete] */ + + CORE_LEAVE(); +} + +/* + * this function elects the future thread to execute, taking care to + * save the currently executing one, should both the task and thread be + * still in a running state. + * + * note that this function may elect the already running thread if + * (i) it has not expired and (ii) there is no thread with a higher priority + * in the active list. + * + * finally, this function may detect that the scheduler has been stopped. + * should this occur, the elected thread is saved and the original kernel + * thread is specially scheduled, hence returning to its initial state. + */ + +t_error scheduler_elect(void) +{ + /* FIXME[code to complete] */ + + CORE_LEAVE(); +} + +/* + * this function adds a thread to the scheduler. + */ + +t_error scheduler_add(i_thread id) +{ + /* FIXME[code to complete] */ + + CORE_LEAVE(); +} + +/* + * this function removes a thread from the scheduler. + */ + +t_error scheduler_remove(i_thread id) +{ + /* FIXME[code to complete] */ + + CORE_LEAVE(); +} + +/* + * this function updates a thread from the scheduler after its priority + * has changed. + * + * note that the easiest way could seem to be to remove then add the + * thread, in which case it would be added to its proper queue depending + * on its new priority. unfortunately this solution would make the thread + * lose its remaining timeslices. besides, if the thread to remove is the + * currently scheduled thread, removing it would incurr a yield. in this + * case, the thread would no longer be scheduled and would therefore not + * have the chance to add itself back to the scheduler. + */ + +t_error scheduler_update(i_thread id) +{ + /* FIXME[code to complete] */ + + CORE_LEAVE(); +} + +/* + * this function returns true if the scheduler for the given CPU identifier + * exists. + */ + +t_error scheduler_exist(i_cpu id) +{ + if (set_exist(_scheduler.schedulers, id) != ERROR_TRUE) + CORE_FALSE(); + + CORE_TRUE(); +} + +/* + * this function retrives the scheduler for the given CPU. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the object from the set of schedulers. + */ + +t_error scheduler_get(i_cpu id, + o_scheduler** object) +{ + /* + * 0) + */ + + if (object == NULL) + CORE_ESCAPE("the 'object' argument is null"); + + /* + * 1) + */ + + if (set_get(_scheduler.schedulers, id, (void**)object) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the object from the set of schedulers"); + + CORE_LEAVE(); +} + +/* + * this function returns the scheduler object for the current CPU. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the current CPU identifier. + * 2) retrieve the current scheduler object. + */ + +t_error scheduler_current(o_scheduler** scheduler) +{ + i_cpu cpu; + + /* + * 0) + */ + + if (scheduler == NULL) + CORE_ESCAPE("the 'scheduler' argument is null"); + + /* + * 1) + */ + + if (cpu_current(&cpu) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the current CPU object"); + + /* + * 2) + */ + + if (set_get(_scheduler.schedulers, cpu, (void**)scheduler) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the scheduler from the set"); + + CORE_LEAVE(); +} + +/* + * this function initializes the scheduler manager. + * + * steps: + * + * 1) display a message. + * 2) initialize the manager's structure. + * 3) initialize the quantum. + * 4) retrieve the number of CPUs. + * 5) reserve the set of schedulers. + * 6) go through the CPUs. + * a) retrieve the CPU object. + * b) build the scheduler object. + * g) add the scheduler to the set of schedulers. + * 7) retrieve the currently running scheduler. + * 8) set the scheduler's current thread as being the kernel thread. + * 9) call the machine. + */ + +t_error scheduler_initialize(void) +{ + o_scheduler* scheduler; + t_setsz ncpus; + s_iterator it; + t_state st; + o_cpu* o; + + /* + * 1) + */ + + module_call(console, message, + '+', "initializing the scheduler manager\n"); + + /* + * 2) + */ + + memset(&_scheduler, 0x0, sizeof (m_scheduler)); + + /* + * 3) + */ + + _scheduler.quantum = SCHEDULER_QUANTUM; + + /* + * 4) + */ + + if (set_size(_cpu.cpus, &ncpus) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the number of active CPUs"); + + /* + * 5) + */ + + if (set_reserve(array, + SET_OPTION_ALLOCATE, + ncpus, + sizeof (o_scheduler), + &_scheduler.schedulers) != ERROR_OK) + CORE_ESCAPE("unable to reserve a set for the schedulers"); + + /* + * 6) + */ + + set_foreach(SET_OPTION_FORWARD, _cpu.cpus, &it, st) + { + o_scheduler scheduler; + + /* + * a) + */ + + if (set_object(_cpu.cpus, it, (void**)&o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the CPU object"); + + /* + * b) + */ + + scheduler.cpu = o->id; + scheduler.thread = ID_UNUSED; + scheduler.timeslice = _scheduler.quantum; + scheduler.priority = 0; + scheduler.state = SCHEDULER_STATE_STOP; + + /* + * g) + */ + + if (set_append(_scheduler.schedulers, &scheduler) != ERROR_OK) + CORE_ESCAPE("unable to append the CPU's scheduler to the set"); + } + + /* + * 7) + */ + + if (scheduler_current(&scheduler) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the current CPU's scheduler"); + + /* + * 8) + */ + + scheduler->thread = _kernel.thread; + + /* + * 9) + */ + + if (machine_call(scheduler, initialize) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function just reinitializes the scheduler manager. + * + * steps: + * + * 1) display a message. + * 2) call the machine. + */ + +t_error scheduler_clean(void) +{ + /* + * 1) + */ + + module_call(console, message, + '+', "cleaning the scheduler manager\n"); + + /* + * 2) + */ + + if (machine_call(scheduler, clean) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +#endif diff --git a/export/data/snapshot/kaneton/machine/architecture/ia32/educational/context.c b/export/data/snapshot/kaneton/machine/architecture/ia32/educational/context.c new file mode 100644 index 0000000..f134aff --- /dev/null +++ b/export/data/snapshot/kaneton/machine/architecture/ia32/educational/context.c @@ -0,0 +1,704 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane.../architecture/ia32/educational/context.c + * + * created renaud voltz [tue apr 4 03:08:03 2006] + * updated julien quintard [mon feb 7 15:53:52 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file contains functions related to the IA32 context management. + * + * the ia32/educational implementation makes use of a single TSS - Task + * State Segment which describes the currently executing context. + * + * the context switch mechanism consists in saving the currently executing + * thread's state and loading the future's. note however that the CPU + * performs some saving/restoring automatically. + * + * basically, things go as follows. a task is running, say in CPL3 i.e a guest + * task. when the timer interrupt occurs, for example, the privilege changes + * from CPL3 to CPL0. the CPU, noticing this change in privilege saves some + * of the thread's context---SS, ESP, EFLAGS, CS, EIP and possible an error + * code---on a special stack referred to as the thread's pile i.e a stack + * specifically used whenever the privilege changes. note that, should no + * change in privilege occur, the registers would be stored on the thread's + * current stack. + * + * at this point, the processor executes the handler shell (cf handler.c) + * which pre-handles an interrupt depending on its nature: exception, IRQ etc. + * besides, the handler shell calls the ARCHITECTURE_CONTEXT_SAVE() + * macro-function which is at the heart of the context switching mechanism. + * + * once the interrupt has been treated, the ARCHITECTURE_CONTEXT_RESTORE() + * macro-function restores the necessary. finally, the 'iret' instruction + * is called. the CPU noticing that a privilege change had occured, restores + * the thread's context by fetching the registers it pushed from the + * thread's pile (or from the thread's stack if the privilege had not changed). + * + * the whole context switch mechanism therefore relies on interrupts. more + * precisely the idea for context switching from a thread A to a thread B + * is to (i) save the thread A's context but this is done naturally when + * A gets interrupted and (ii) change the TSS so that thread B gets referenced, + * hence making the CPU think B got interrupted (instead of A). therefore, + * when returning from the interrupt, the CPU will restore B's context + * rather than A's. this is how A got interrupted, its context saved + * and B's execution got resumed. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the architecture manager. + */ + +extern am _architecture; + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * kernel manager. + */ + +extern m_kernel _kernel; + +/* + * thread manager. + */ + +extern m_thread _thread; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function dumps the given context. + */ + +t_error architecture_context_dump(as_context context) +{ + module_call(console, message, + '#', + "context: ds(0x%x) edi(0x%x) esi(0x%x) ebp(0x%x) _esp(0x%x)\n", + context.ds & 0xffff, + context.edi, + context.esi, + context.ebp, + context._esp); + + module_call(console, message, + '#', + " ebx(0x%x) edx(0x%x) ecx(0x%x) eax(0x%x) error(0x%x)\n", + context.ebx, + context.edx, + context.ecx, + context.eax, + context.error); + + module_call(console, message, + '#', + " eip(0x%x) cs(0x%x) eflags(0x%x) esp(0x%x) ss(0x%x)\n", + context.eip, + context.cs & 0xffff, + context.eflags, + context.esp, + context.ss & 0xffff); + + MACHINE_LEAVE(); +} + +/* + * this function builds the given context, initializes its attributes. + * + * steps: + * + * 1) retrieve the thread and task objects. + * 2) depending on the thread's task class. + * A) if this thread is a kernel thread i.e a ring0 thread, this means + * that its context will be saved on its stack. besides, since no change + * in privilege will occur when interrupted, there is no need for a + * pile... + * a) set the pile attributes to zero. + * B) otherwise... + * a) set the thread's pile size. + * b) allocate a pile for the thread i.e a stack which is used by the + * processor to store the context whenever the execution privilege + * changes; for example whenever a guest task running in CP3 is + * interrupted by the timer. + * c) set the pile pointer to the end since IA32 stacks grow towards + * the lower addresses. + * 3) initialize the IA32 context's registers to zero. + * 4) initialize the eflags by activating the first bit (mandatory) but + * also the IF flags to that maskable interrupts get triggered. besides, + * allow driver tasks to perform I/O operations by setting the + * appropriate IOPL. + * 5) set the context's segment selectors according to the task's class. + * 6) complete the thread's initial IA32 context by setting ESP and EIP. + * 7) set the static stack pointer to the end since stacks grow towards + * the lower addresses. + * 8) depending on the thread's task class. + * A) if the thread is a kernel thread, set the initial context's position + * on the stack. indeed, let's recall that ring0 thread's contexts + * are stored on their stack since no change in privilege occurs. + * B) otherwise, set its position on the thread's pile i.e special kernel + * stack. + * 9) finally set the thread's initial IA32 context. note that this step + * is ignored for the kernel thread. indeed, this thread is the one + * setting up the whole kernel. once the scheduler is started, an interrupt + * will be triggered hence interrupting the running thread i.e the kernel + * thread, hence saving its context. since this special thread will always + * start with its context being saved, there is no need to do it now. + * on the contrary, the other threads will begin with their context being + * restored in order to be scheduled for the first time. + */ + +t_error architecture_context_build(i_thread id) +{ + o_task* task; + o_thread* thread; + as_context ctx; + + /* + * 1) + */ + + if (thread_get(id, &thread) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the thread object"); + + if (task_get(thread->task, &task) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + if (task->class == TASK_CLASS_KERNEL) + { + /* + * A) + */ + + /* + * a) + */ + + thread->machine.pile.base = 0x0; + thread->machine.pile.size = 0x0; + thread->machine.pile.pointer = 0x0; + } + else + { + /* + * B) + */ + + /* + * a) + */ + + thread->machine.pile.size = ARCHITECTURE_HANDLER_PILE_SIZE; + + /* + * b) + */ + + if (map_reserve(task->as, + MAP_OPTION_NONE, + thread->machine.pile.size, + PERMISSION_READ | PERMISSION_WRITE, + &thread->machine.pile.base) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve a map for the thread's pile"); + + /* + * c) + */ + + thread->machine.pile.pointer = + thread->machine.pile.base + thread->machine.pile.size - 16; + } + + /* + * 3) + */ + + memset(&ctx, 0x0, sizeof (as_context)); + + /* + * 4) + */ + + ctx.eflags = + ARCHITECTURE_REGISTER_EFLAGS_01 | + ARCHITECTURE_REGISTER_EFLAGS_IF; + + if (task->class == TASK_CLASS_DRIVER) + { + ctx.eflags |= + ARCHITECTURE_REGISTER_EFLAGS_IOPL_SET(ARCHITECTURE_PRIVILEGE_DRIVER); + } + + /* + * 5) + */ + + switch (task->class) + { + case TASK_CLASS_KERNEL: + { + ctx.cs = _thread.machine.selectors.kernel.cs; + ctx.ds = _thread.machine.selectors.kernel.ds; + ctx.ss = _thread.machine.selectors.kernel.ds; + + break; + } + case TASK_CLASS_DRIVER: + { + ctx.cs = _thread.machine.selectors.driver.cs; + ctx.ds = _thread.machine.selectors.driver.ds; + ctx.ss = _thread.machine.selectors.driver.ds; + + break; + } + case TASK_CLASS_SERVICE: + { + ctx.cs = _thread.machine.selectors.service.cs; + ctx.ds = _thread.machine.selectors.service.ds; + ctx.ss = _thread.machine.selectors.service.ds; + + break; + } + case TASK_CLASS_GUEST: + { + ctx.cs = _thread.machine.selectors.guest.cs; + ctx.ds = _thread.machine.selectors.guest.ds; + ctx.ss = _thread.machine.selectors.guest.ds; + + break; + } + } + + /* + * 6) + */ + + ctx.esp = thread->stack.base + thread->stack.size - 16; + ctx.eip = thread->entry; + + /* + * 7) + */ + + thread->machine.stack.pointer = thread->stack.base + thread->stack.size - 16; + + /* + * 8) + */ + + if (task->class == TASK_CLASS_KERNEL) + { + /* + * A) + */ + + thread->machine.context = + thread->machine.stack.pointer - sizeof (as_context); + } + else + { + /* + * B) + */ + + thread->machine.context = + thread->machine.pile.pointer - sizeof (as_context); + } + + /* + * 9) + */ + + if (thread->id != _kernel.thread) + { + if (architecture_context_set(thread->id, &ctx) != ERROR_OK) + MACHINE_ESCAPE("unable to set the context"); + } + + MACHINE_LEAVE(); +} + +/* + * this function destroys a thread's context. + * + * steps: + * + * 1) retrieve the thread and task object. + * 2) if the thread has a pile---i.e is not a kernel thread---release it. + */ + +t_error architecture_context_destroy(i_thread id) +{ + o_task* task; + o_thread* thread; + + /* + * 1) + */ + + if (thread_get(id, &thread) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the thread object"); + + if (task_get(thread->task, &task) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + if (task->class != TASK_CLASS_KERNEL) + { + if (map_release(task->as, + thread->machine.pile.base) != ERROR_OK) + MACHINE_ESCAPE("unable to release the thread's pile"); + } + + MACHINE_LEAVE(); +} + +/* + * this function sets up the context switch mechanism. + * + * steps: + * + * 1) retrieve the kernel address space object. + * 2) reserve the KIS - Kernel Interrupt Stack and set its pointer to + * the end since stacks grow towards low addresses. note that this + * special stack resides within the kernel, as its name indicates, and + * is used to handle interrupts. + * 3) reserve a memory area for the TSS. + * 4) build the initial TSS. + * 5) update the TSS in order to represent the currently executing thread. + * since the current thread is the kernel thread, which runs in ring0, + * no ring0 stack needs to be provided, hence SS0 and ESP0 are ignored. + * 6) activate the TSS. + * 7) retrieve the segment selectors associated with the + * kernel/driver/service/guest code/data segments. these segment selectors + * will be used whenever a thread of the associated task class will + * be created. + */ + +t_error architecture_context_setup(void) +{ + as_tss* tss; + o_as* as; + + /* + * 1) + */ + + if (as_get(_kernel.as, &as) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the address space object"); + + /* + * 2) + */ + + _architecture.kernel.kis.size = ARCHITECTURE_HANDLER_KIS_SIZE; + + if (map_reserve(_kernel.as, + MAP_OPTION_SYSTEM, + _architecture.kernel.kis.size, + PERMISSION_READ | PERMISSION_WRITE, + &_architecture.kernel.kis.base) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve the TSS memory area"); + + _architecture.kernel.kis.pointer = + _architecture.kernel.kis.base + (_architecture.kernel.kis.size - 16); + + /* + * 3) + */ + + if (map_reserve(_kernel.as, + MAP_OPTION_SYSTEM, + ARCHITECTURE_TSS_SIZE, + PERMISSION_READ | PERMISSION_WRITE, + &_thread.machine.tss) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve the TSS memory area"); + + /* + * 4) + */ + + if (architecture_tss_build(_thread.machine.tss, &tss) != ERROR_OK) + MACHINE_ESCAPE("unable to build the initial TSS"); + + /* + * 5) + */ + + if (architecture_tss_update(tss, + ARCHITECTURE_TSS_SS0_NULL, + ARCHITECTURE_TSS_ESP0_NULL, + ARCHITECTURE_TSS_IO) != ERROR_OK) + MACHINE_ESCAPE("unable to build the TSS"); + + /* + * 6) + */ + + if (architecture_tss_activate(tss) != ERROR_OK) + MACHINE_ESCAPE("unable to activate the system's TSS"); + + /* + * 7) + */ + + if (architecture_gdt_selector( + ARCHITECTURE_GDT_INDEX_KERNEL_CODE, + ARCHITECTURE_PRIVILEGE_KERNEL, + &_thread.machine.selectors.kernel.cs) != ERROR_OK) + MACHINE_ESCAPE("unable to build the kernel code segment selector"); + + if (architecture_gdt_selector( + ARCHITECTURE_GDT_INDEX_KERNEL_DATA, + ARCHITECTURE_PRIVILEGE_KERNEL, + &_thread.machine.selectors.kernel.ds) != ERROR_OK) + MACHINE_ESCAPE("unable to build the kernel data segment selector"); + + if (architecture_gdt_selector( + ARCHITECTURE_GDT_INDEX_DRIVER_CODE, + ARCHITECTURE_PRIVILEGE_DRIVER, + &_thread.machine.selectors.driver.cs) != ERROR_OK) + MACHINE_ESCAPE("unable to build the driver code segment selector"); + + if (architecture_gdt_selector( + ARCHITECTURE_GDT_INDEX_DRIVER_DATA, + ARCHITECTURE_PRIVILEGE_DRIVER, + &_thread.machine.selectors.driver.ds) != ERROR_OK) + MACHINE_ESCAPE("unable to build the driver data segment selector"); + + if (architecture_gdt_selector( + ARCHITECTURE_GDT_INDEX_SERVICE_CODE, + ARCHITECTURE_PRIVILEGE_SERVICE, + &_thread.machine.selectors.service.cs) != ERROR_OK) + MACHINE_ESCAPE("unable to build the service code segment selector"); + + if (architecture_gdt_selector( + ARCHITECTURE_GDT_INDEX_SERVICE_DATA, + ARCHITECTURE_PRIVILEGE_SERVICE, + &_thread.machine.selectors.service.ds) != ERROR_OK) + MACHINE_ESCAPE("unable to build the service data segment selector"); + + if (architecture_gdt_selector( + ARCHITECTURE_GDT_INDEX_GUEST_CODE, + ARCHITECTURE_PRIVILEGE_GUEST, + &_thread.machine.selectors.guest.cs) != ERROR_OK) + MACHINE_ESCAPE("unable to build the guest code segment selector"); + + if (architecture_gdt_selector( + ARCHITECTURE_GDT_INDEX_GUEST_DATA, + ARCHITECTURE_PRIVILEGE_GUEST, + &_thread.machine.selectors.guest.ds) != ERROR_OK) + MACHINE_ESCAPE("unable to build the guest data segment selector"); + + MACHINE_LEAVE(); +} + +/* + * this function switches execution to the specified thread. + */ + +t_error architecture_context_switch(i_thread current, + i_thread future) +{ + /* FIXME[code to complete] */ + + MACHINE_LEAVE(); +} + +/* + * this function pushes the given arguments on a thread's stack. this way, + * the thread will be able to access these values at startup. + * + * steps: + * + * 1) retrieve the thread and task objects. + * 2) retrieve the thread's context. + * 3) update the thread's stack pointer by decreasing it since the + * arguments are going to be stored at the top of it. + * 4) write the arguments to the thread's stack. + * 5) update the thread's context. + */ + +t_error architecture_context_arguments(i_thread id, + void* arguments, + t_vsize size) +{ + s_thread_context context; + o_thread* thread; + o_task* task; + + /* + * 1) + */ + + if (thread_get(id, &thread) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the thread object"); + + if (task_get(thread->task, &task) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + if (thread_store(thread->id, &context) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the thread context"); + + /* + * 3) + */ + + context.sp -= size; + + /* + * 4) + */ + + if (as_write(task->as, arguments, size, context.sp) != ERROR_OK) + MACHINE_ESCAPE("unable to store the arguments on the thread's stack"); + + /* + * 5) + */ + + if (thread_load(thread->id, context) != ERROR_OK) + MACHINE_ESCAPE("unable to update the thread context"); + + MACHINE_LEAVE(); +} + +/* + * this function retrieves the IA32 context of the given thread. + * + * note that the interrupted task's context has been stored in its + * pile, i.e ring0 stack (excepts for kernels threads). since the pile is + * only mapped in the task's address space, this function, running in the + * kernel environment, cannot access it directly. therefore, the as_read() + * function is used to temporarily map the necessary pages in the kernel + * address space. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the thread and task objects. + * 2) read the thread's context from its pile. + */ + +t_error architecture_context_get(i_thread id, + as_context* context) +{ + o_thread* thread; + o_task* task; + + /* + * 0) + */ + + if (context == NULL) + MACHINE_ESCAPE("the 'context' argument is null"); + + /* + * 1) + */ + + if (thread_get(id, &thread) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the thread object"); + + if (task_get(thread->task, &task) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + if (as_read(task->as, + thread->machine.context, + sizeof (as_context), + context) != ERROR_OK) + MACHINE_ESCAPE("unable to read the thread's IA32 context"); + + MACHINE_LEAVE(); +} + +/* + * this function updates the context of a given thread. + * + * note that the interrupted task's context has been stored in its + * pile, i.e ring0 stack (except for kernel threads). since the pile is + * only mapped in the task's address space, this function, running in the + * kernel environment, cannot access it directly. therefore, the as_write() + * function is used to temporarily map the necessary pages in the kernel + * address space. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the thread and task objects. + * 2) update the thread's context stored in its pile by writing its address + * space. + */ + +t_error architecture_context_set(i_thread id, + as_context* context) +{ + o_thread* thread; + o_task* task; + + /* + * 0) + */ + + if (context == NULL) + MACHINE_ESCAPE("the 'context' argument is null"); + + /* + * 1) + */ + + if (thread_get(id, &thread) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the thread object"); + + if (task_get(thread->task, &task) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + if (as_write(task->as, + context, + sizeof (as_context), + thread->machine.context) != ERROR_OK) + MACHINE_ESCAPE("unable to write the thread's IA32 context"); + + MACHINE_LEAVE(); +} diff --git a/export/data/snapshot/kaneton/machine/architecture/ia32/educational/environment.c b/export/data/snapshot/kaneton/machine/architecture/ia32/educational/environment.c new file mode 100644 index 0000000..d8a42f5 --- /dev/null +++ b/export/data/snapshot/kaneton/machine/architecture/ia32/educational/environment.c @@ -0,0 +1,444 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...hitecture/ia32/educational/environment.c + * + * created julien quintard [thu jan 13 23:13:50 2011] + * updated julien quintard [tue apr 12 07:40:26 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file contains functions for initializing the environments of a + * task, especially regarding its address space. + * + * note that the kernel is treated separately from the servers i.e drivers, + * services and guests. + * + * [XXX:improvement] in the server initialization, needless to map the kernel + * code and stack. instead the handler shells should be + * mapped i.e the .handler section. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the init structure. + */ + +extern s_init* _init; + +/* + * the kernel manager. + */ + +extern m_kernel _kernel; + +/* + * the thread manager. + */ + +extern m_thread _thread; + +/* + * the segment manager. + */ + +extern m_segment _segment; + +/* + * the architecture manager. + */ + +extern am _architecture; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function initializes the kernel's environment. + * + * steps: + * + * 1) retrieve the address space objec. + * 2) set the kernel address space's page directory by importing the + * page directory set up by the boot loader. + * 3) generate the PDBR - Page Directory Base Register, also known as + * the CR3, based on the kernel page directory's physical address and + * some flags. + * 4) set the page directory virtual address as being an identity mapping + * of the physical address. this is how the boot loader set things up. + * 5) set the current page directory as being the kernel's one by updating + * the microprocessor CR3 register. + * 6) update the kernel page directory---which is assumed to have been mapped + * by the boot loader through the identity mapping technique---in order + * to set up the mirroring entry. this entry wastes the last 4MB + * of memory and are used for accessing the kernel page directory and + * tables without mapping anything, hence preventing infinite loops. + * note that the entry references the page directory itself, making the + * page directory act as a page table whenever accessed through the + * mirror page directory entry. + * 7) the last 4MB of virtual memory are not accessible since the mirroring + * page directory entry and the referenced page table's entries---the + * page directory itself---are used for the mirroring mechanism. these + * 4MB are wasted and must therefore not be reservable or the kernel may + * end up overwritten the mirroring entries. a region covering the last + * 4MB of virtual memory is therefore injected. + * note that the region injected references an ID_UNUSED-identified + * segment in order to avoid having to reserve a 4MB segment. this is + * possible because region_inject() does not check if the referenced + * segment identifer is valid. + * 8) this step consists in cleaning the page directory set up by the boot + * loader, now used by the kernel, by unmapping any page which is not + * related to the fundamental regions provided by the boot loader. + * go through all the pre-reserved regions provided by the bootloader plus + * one. this additional iteration is required in order to clean the + * mapped pages from the last region to the end of the virtual address + * space. + * a) compute the page directory and table end indexes for the given + * region. note that for the extra iteration, the end indexes are + * set to their maximum so that every page table entry of every page + * directory entry following the last region is cleaned. + * b) go through the involved page directory entries. + * i) if the page directory entry does not reference a page table or + * is used as the mirroring entry, leave it. otherwise... + * #1) retrieve the page table referenced by the page directory entry. + * note that the boot loader relied on the identity mapping technique + * for its paging set up. identity mapping is therefore used to + * retrieve the page table virtual address. + * #2) go through the page table's involved entries. + * #a) if the page table entry is used, delete the reference as + * this mapping must not be very important since not related to + * the pre-reserved regions provided by the boot loader. + * c) if the treated region is not the extra one, compute the next + * page directory and table start indexes as starting right after + * the end of the region i.e address + size. + * 9) flush the whole TLB, resetting all the address translations. + * 10) register the kernel PDBR as being the PDBR on which to switch whenever + * an interrupt occurs. + */ + +t_error architecture_environment_kernel(i_as id) +{ + i_region useless; + at_cr3 pdbr; + o_as* as; + o_region* r; + + /* + * 1) + */ + + if (as_get(id, &as) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the address space object"); + + /* + * 2) + */ + + as->machine.pd = _init->machine.pd; + + /* + * 3) + */ + + if (architecture_paging_pdbr(as->machine.pd, + ARCHITECTURE_REGISTER_CR3_PCE | + ARCHITECTURE_REGISTER_CR3_PWB, + &pdbr) != ERROR_OK) + MACHINE_ESCAPE("unable to build the CR3 register's content"); + + /* + * 5) + */ + + /* FIXME[make the page directory provided by the boot loader the system's + current page directory by updating the necessary IA32 hardware + structure and possibly storing the value in a globally accessible + variable such as a manager] */ + + /* + * 6) + */ + + /* FIXME[create the mirroring entry by adding the page directory's + address] */ + + /* + * 7) + */ + + if ((r = malloc(sizeof (o_region))) == NULL) + MACHINE_ESCAPE("unable to allocate memory for the region object"); + + r->address = ARCHITECTURE_PAGING_ADDRESS(ARCHITECTURE_PD_MIRROR, 0); + r->segment = ID_UNUSED; + r->offset = 0x0; + r->size = ARCHITECTURE_PT_SIZE * ___kaneton$pagesz; + r->options = REGION_OPTION_NONE; + + if (region_inject(as->id, r, &useless) != ERROR_OK) + MACHINE_ESCAPE("unable to inject the mirroring region"); + + /* + * 8) + */ + + /* FIXME[go through the registered regions and remove the + page table entries which do not correspond to these + regions. this is necessary because the boot loader + mapped an awful lot of pages which must now be cleaned] + + pde.start = 0; + pte.start = 0; + + for (i = 0; i < (_init->nregions + 1); i++) + { + if (i != _init->nregions) + { + pde.end = ARCHITECTURE_PD_INDEX(_init->regions[i].address); + pte.end = ARCHITECTURE_PT_INDEX(_init->regions[i].address); + } + else + { + pde.end = ARCHITECTURE_PD_SIZE - 1; + pte.end = ARCHITECTURE_PT_SIZE; + } + + for (pde.index = pde.start; + pde.index <= pde.end; + pde.index++) + { + if ((pde.index != ARCHITECTURE_PD_MIRROR) && + (pd[pde.index] & ARCHITECTURE_PDE_PRESENT)) + { + pt = (at_pt)ARCHITECTURE_PDE_ADDRESS(pd[pde.index]); + + for (pte.index = (pde.index == pde.start ? pte.start : 0); + pte.index < (pde.index == pde.end ? + pte.end : ARCHITECTURE_PT_SIZE); + pte.index++) + { + if (pt[pte.index] & ARCHITECTURE_PTE_PRESENT) + { + if (architecture_pt_delete(pt, pte.index) != ERROR_OK) + MACHINE_ESCAPE("unable to delete the page " + "table entry"); + } + } + } + } + + if (i != _init->nregions) + { + pde.start = ARCHITECTURE_PD_INDEX(_init->regions[i].address + + _init->regions[i].size); + pte.start = ARCHITECTURE_PT_INDEX(_init->regions[i].address + + _init->regions[i].size); + } + } + */ + + /* + * 9) + */ + + if (architecture_tlb_flush() != ERROR_OK) + MACHINE_ESCAPE("unable to flush the TLB"); + + /* + * 10) + */ + + _architecture.kernel.pdbr = pdbr; + + MACHINE_LEAVE(); +} + +/* + * this function sets up the environment of a server i.e drivers, services + * and guests. + * + * steps: + * + * 1) retrieve the address space object. + * 2) reserve a system segment. + * 3) use this segment for the given address space's page directory. + * 4) map the page directory, initialize it and unmap it. + * 5) locate the segment containing the system's TSS and map it in + * the given address space. note that the TSS is mapped at the same + * virtual address as in the kernel. + * 6) locate the segment containing the system's GDT and map it in the + * given address space, again at the same virtual address as the kernel's. + * 7) locate the segment containing the system's IDT and map it in the + * given address space, again at the same virtual address as the kernel's. + * 8) locate the segment containing the kernel code and map it in the given + * address space. note that the identity mapping technique is used here. + * 9) locate the segment containing the kernel stack and map it in the given + * address space, note that the identity mapping technique is used here. + */ + +t_error architecture_environment_server(i_as id) +{ + i_segment segment; + i_region region; + o_as* as; + o_region* r; + o_segment* s; + + /* + * 1) + */ + + if (as_get(id, &as) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the address space object"); + + /* + * 2) + */ + + if (segment_reserve(as->id, + ___kaneton$pagesz, + PERMISSION_READ | PERMISSION_WRITE, + SEGMENT_OPTION_SYSTEM, + &segment) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve a segment"); + + if (segment_get(segment, &s) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the segment object"); + + /* + * 3) + */ + + as->machine.pd = s->address; + + /* + * 4) + */ + + /* FIXME[map the server's page directory, initialize it and + unmap it] */ + + /* + * 5) + */ + + if (region_locate(_kernel.as, + _thread.machine.tss, + ®ion) == ERROR_FALSE) + MACHINE_ESCAPE("unable to locate the region in which the TSS lies"); + + if (region_get(_kernel.as, region, &r) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the region object"); + + if (region_reserve(as->id, + r->segment, + 0x0, + REGION_OPTION_FORCE | + REGION_OPTION_NONE, + _thread.machine.tss, + r->size, + ®ion) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve the region mapping the TSS"); + + /* + * 6) + */ + + if (region_locate(_kernel.as, + (t_vaddr)_segment.machine.gdt.table, + ®ion) == ERROR_FALSE) + MACHINE_ESCAPE("unable to locate the region in which the GDT lies"); + + if (region_get(_kernel.as, region, &r) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the region object"); + + if (region_reserve(as->id, + r->segment, + 0x0, + REGION_OPTION_FORCE | + REGION_OPTION_NONE, + (t_vaddr)_segment.machine.gdt.table, + ___kaneton$pagesz, + ®ion) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve the region mapping the GDT"); + + /* + * 7) + */ + + /* FIXME[reserve a region for the system's IDT very much as for + the GDT above] */ + + /* + * 8) + */ + + /* XXX + if (region_reserve(asid, + _init->kcode, + LINKER_SYMBOL(_handler_begin) - _init->kcode, + REGION_OPTION_FORCE | REGION_OPTION_PRIVILEGED, + LINKER_SYMBOL(_handler_begin), + LINKER_SYMBOL(_handler_end) - + LINKER_SYMBOL(_handler_begin), + ®) != ERROR_OK) + + if (region_reserve(asid, + _init->kcode, + LINKER_SYMBOL(_handler_data_begin) - _init->kcode, + REGION_OPTION_FORCE | REGION_OPTION_PRIVILEGED, + LINKER_SYMBOL(_handler_data_begin), + LINKER_SYMBOL(_handler_data_end) - + LINKER_SYMBOL(_handler_data_begin), + ®) != ERROR_OK) + */ + + if (segment_locate(_init->kcode, &segment) == ERROR_FALSE) + MACHINE_ESCAPE("unable to locate the segment which contains the " + "kernel code"); + + if (region_reserve(as->id, + segment, + 0x0, + REGION_OPTION_FORCE, + (t_vaddr)_init->kcode, + (t_vsize)_init->kcodesz, + ®ion) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve the region mapping the kernel code"); + + /* + * 9) + */ + + if (segment_locate(_init->kstack, + &segment) == ERROR_FALSE) + MACHINE_ESCAPE("unable to locate the segment which contains the " + "kernel stack"); + + if (region_reserve(as->id, + segment, + 0x0, + REGION_OPTION_FORCE, + (t_vaddr)_init->kstack, + (t_vsize)_init->kstacksz, + ®ion) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve the region mapping the kernel stack"); + + MACHINE_LEAVE(); +} diff --git a/export/data/snapshot/kaneton/machine/architecture/ia32/educational/handler.c b/export/data/snapshot/kaneton/machine/architecture/ia32/educational/handler.c new file mode 100644 index 0000000..63002b8 --- /dev/null +++ b/export/data/snapshot/kaneton/machine/architecture/ia32/educational/handler.c @@ -0,0 +1,24 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane.../architecture/ia32/educational/handler.c + * + * created renaud voltz [thu feb 23 10:49:43 2006] + * updated julien quintard [mon apr 11 13:44:48 2011] + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* FIXME[complete if necessary] */ diff --git a/export/data/snapshot/kaneton/machine/architecture/ia32/educational/idt.c b/export/data/snapshot/kaneton/machine/architecture/ia32/educational/idt.c new file mode 100644 index 0000000..3b857f2 --- /dev/null +++ b/export/data/snapshot/kaneton/machine/architecture/ia32/educational/idt.c @@ -0,0 +1,34 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...hine/architecture/ia32/educational/idt.c + * + * created renaud voltz [sun feb 12 02:02:19 2006] + * updated julien quintard [mon apr 11 13:44:56 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file provides functionalities for managing the IDT - Interrupt + * Descriptor Table. + * + * for more information regarding the handlers triggered through the IDT, + * please have a look at the handler.c file. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* FIXME[complete if necessary] */ diff --git a/export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/context.h b/export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/context.h new file mode 100644 index 0000000..13fd0df --- /dev/null +++ b/export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/context.h @@ -0,0 +1,117 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...cture/ia32/educational/include/context.h + * + * created renaud voltz [tue apr 4 22:01:00 2006] + * updated julien quintard [mon feb 7 16:19:17 2011] + */ + +#ifndef ARCHITECTURE_CONTEXT_H +#define ARCHITECTURE_CONTEXT_H 1 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function saves the context of the thread which had just + * been interrupted. + * + * note that at this point, the stack in use is the thread's pile i.e ring0 + * stack; except for the kernel threads since there is no change in privilege. + */ + +#define ARCHITECTURE_CONTEXT_SAVE() \ + /* FIXME[code to complete] */ + +/* + * this macro-function restores the context of the thread whose PDBR and pile + * are referenced in _architecture.thread. as such, the whole ia32/educational + * context switch mechanism relies on the simple fact that changing + * the _architecture structure and returning from the interrupt will make + * the thread's context restored and its execution resumed. + * + * note that at this point, the environment is composed of the kernel PDBR + * and the KIS - Kernel Interrupt Stack. + */ + +#define ARCHITECTURE_CONTEXT_RESTORE() \ + /* FIXME[code to complete] */ + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * this structure represents the IA32 context. + */ + +typedef struct +{ + t_reg32 ds; + t_reg32 edi; + t_reg32 esi; + t_reg32 ebp; + t_reg32 _esp; + t_reg32 ebx; + t_reg32 edx; + t_reg32 ecx; + t_reg32 eax; + t_reg32 error; + t_reg32 eip; + t_reg32 cs; + t_reg32 eflags; + t_reg32 esp; + t_reg32 ss; +} __attribute__ ((packed)) as_context; + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../context.c + */ + +/* + * ../context.c + */ + +t_error architecture_context_dump(as_context context); + +t_error architecture_context_build(i_thread id); + +t_error architecture_context_destroy(i_thread id); + +t_error architecture_context_setup(void); + +t_error architecture_context_locate(void); + +t_error architecture_context_switch(i_thread current, + i_thread future); + +t_error architecture_context_arguments(i_thread id, + void* arguments, + t_vsize size); + +t_error architecture_context_get(i_thread id, + as_context* context); + +t_error architecture_context_set(i_thread id, + as_context* context); + + +/* + * eop + */ + +#endif diff --git a/export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/handler.h b/export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/handler.h new file mode 100644 index 0000000..1bbf8c1 --- /dev/null +++ b/export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/handler.h @@ -0,0 +1,56 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...cture/ia32/educational/include/handler.h + * + * created renaud voltz [fri feb 17 16:48:22 2006] + * updated julien quintard [mon apr 11 13:45:51 2011] + */ + +#ifndef ARCHITECTURE_HANDLER_H +#define ARCHITECTURE_HANDLER_H 1 + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * this macro represents the number of handlers the system must set up, + * for the exceptions, IRQs, IPIs and syscalls. + */ + +#define ARCHITECTURE_HANDLER_SIZE ARCHITECTURE_IDT_EXCEPTION_SIZE + \ + ARCHITECTURE_IDT_IRQ_SIZE + \ + ARCHITECTURE_IDT_IPI_SIZE + \ + ARCHITECTURE_IDT_SYSCALL_SIZE + +/* + * this macro defines the size of a thread's pile i.e the stack used whenever + * a privilege change occurs. + */ + +#define ARCHITECTURE_HANDLER_PILE_SIZE ___kaneton$pagesz + +/* + * this macro defines the size of the KIS - Kernel Interrupt Stack. this is + * the stack which is used, within the kernel environment, for treating + * interrupts. + */ + +#define ARCHITECTURE_HANDLER_KIS_SIZE 2 * ___kaneton$pagesz + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../handler.c + */ + +/* + * eop + */ + +#endif diff --git a/export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/idt.h b/export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/idt.h new file mode 100644 index 0000000..f46f938 --- /dev/null +++ b/export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/idt.h @@ -0,0 +1,121 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...hitecture/ia32/educational/include/idt.h + * + * created renaud voltz [fri feb 10 16:36:20 2006] + * updated julien quintard [mon apr 11 13:45:20 2011] + */ + +#ifndef ARCHITECTURE_IDT_H +#define ARCHITECTURE_IDT_H 1 + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * these macros define the base entry and the number of entries for the + * several types of gate: IRQ, exception, IPI or syscall. + * + * note that 200 syscalls could be set up but the kernel limits itself + * to ten which is enough for a microkernel. + */ + +#define ARCHITECTURE_IDT_EXCEPTION_BASE 0 +#define ARCHITECTURE_IDT_EXCEPTION_SIZE 32 + +#define ARCHITECTURE_IDT_IRQ_BASE 32 +#define ARCHITECTURE_IDT_IRQ_SIZE 16 + +#define ARCHITECTURE_IDT_IPI_BASE 48 +#define ARCHITECTURE_IDT_IPI_SIZE 8 + +#define ARCHITECTURE_IDT_SYSCALL_BASE 56 +#define ARCHITECTURE_IDT_SYSCALL_SIZE 10 + +/* + * these macro define some of the exception handler sources. + */ + +#define ARCHITECTURE_IDT_EXCEPTION_DE \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 0 +#define ARCHITECTURE_IDT_EXCEPTION_DB \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 1 +#define ARCHITECTURE_IDT_EXCEPTION_BP \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 3 +#define ARCHITECTURE_IDT_EXCEPTION_OF \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 4 +#define ARCHITECTURE_IDT_EXCEPTION_BR \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 5 +#define ARCHITECTURE_IDT_EXCEPTION_UD \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 6 +#define ARCHITECTURE_IDT_EXCEPTION_NM \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 7 +#define ARCHITECTURE_IDT_EXCEPTION_DF \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 8 +#define ARCHITECTURE_IDT_EXCEPTION_TS \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 10 +#define ARCHITECTURE_IDT_EXCEPTION_NP \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 11 +#define ARCHITECTURE_IDT_EXCEPTION_SS \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 12 +#define ARCHITECTURE_IDT_EXCEPTION_GP \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 13 +#define ARCHITECTURE_IDT_EXCEPTION_PF \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 14 +#define ARCHITECTURE_IDT_EXCEPTION_MF \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 16 +#define ARCHITECTURE_IDT_EXCEPTION_AC \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 17 +#define ARCHITECTURE_IDT_EXCEPTION_MC \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 18 +#define ARCHITECTURE_IDT_EXCEPTION_XM \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 19 + +/* + * these macro define some of the IRQ handler sources. + */ + +#define ARCHITECTURE_IDT_IRQ_PIT \ + ARCHITECTURE_IDT_IRQ_BASE + 0 +#define ARCHITECTURE_IDT_IRQ_KEYBOARD \ + ARCHITECTURE_IDT_IRQ_BASE + 1 +#define ARCHITECTURE_IDT_IRQ_CASCADE \ + ARCHITECTURE_IDT_IRQ_BASE + 2 +#define ARCHITECTURE_IDT_IRQ_COM2 \ + ARCHITECTURE_IDT_IRQ_BASE + 3 +#define ARCHITECTURE_IDT_IRQ_COM1 \ + ARCHITECTURE_IDT_IRQ_BASE + 4 +#define ARCHITECTURE_IDT_IRQ_FLOPPY \ + ARCHITECTURE_IDT_IRQ_BASE + 6 +#define ARCHITECTURE_IDT_IRQ_SPURIOUS \ + ARCHITECTURE_IDT_IRQ_BASE + 7 +#define ARCHITECTURE_IDT_IRQ_RTC \ + ARCHITECTURE_IDT_IRQ_BASE + 8 +#define ARCHITECTURE_IDT_IRQ_ATA1 \ + ARCHITECTURE_IDT_IRQ_BASE + 14 +#define ARCHITECTURE_IDT_IRQ_ATA2 \ + ARCHITECTURE_IDT_IRQ_BASE + 15 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../idt.c + */ + +/* + * eop + */ + +#endif diff --git a/export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/paging.h b/export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/paging.h new file mode 100644 index 0000000..73eb8e2 --- /dev/null +++ b/export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/paging.h @@ -0,0 +1,83 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...ecture/ia32/educational/include/paging.h + * + * created julien quintard [fri feb 11 03:04:40 2005] + * updated julien quintard [mon apr 11 13:48:17 2011] + */ + +#ifndef ARCHITECTURE_PAGING_H +#define ARCHITECTURE_PAGING_H 1 + +/* + * ---------- macro-functions ------------------------------------------------- + */ + +/* + * computes a virtual address according to the given directory and table + * entries. + */ + +#define ARCHITECTURE_PAGING_ADDRESS(_pdei_, _ptei_) \ + (t_vaddr)(((_pdei_) << 22) | ((_ptei_) << 12)) + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +#include + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../paging.c + */ + +/* + * ../paging.c + */ + +t_error architecture_paging_pdbr(t_paddr pd, + t_flags flags, + at_cr3* pdbr); + +t_error architecture_paging_map(i_as id, + i_segment segment, + t_paddr offset, + t_options options, + t_vaddr address, + t_vsize size); + +t_error architecture_paging_unmap(i_as id, + t_vaddr address, + t_vsize size); + +t_error architecture_paging_read(i_segment id, + t_paddr offset, + void* buffer, + t_psize size); + +t_error architecture_paging_write(i_segment id, + t_paddr offset, + const void* buffer, + t_psize size); + +t_error architecture_paging_copy(i_region dst, + t_paddr to, + i_region src, + t_paddr from, + t_psize size); + + +/* + * eop + */ + +#endif diff --git a/export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/pd.h b/export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/pd.h new file mode 100644 index 0000000..83f8986 --- /dev/null +++ b/export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/pd.h @@ -0,0 +1,49 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/Down...chitecture/ia32/educational/include/pd.h + * + * created julien quintard [mon jan 10 09:05:19 2011] + * updated julien quintard [mon apr 11 13:20:03 2011] + */ + +#ifndef ARCHITECTURE_PD_H +#define ARCHITECTURE_PD_H 1 + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * this value defines the page directory entry which acts as the mirror + * entry i.e the entry referencing the page directory itself. + */ + +#define ARCHITECTURE_PD_MIRROR 1023 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../pd.c + */ + +/* + * ../pd.c + */ + + +/* + * eop + */ + +#endif diff --git a/export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/pt.h b/export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/pt.h new file mode 100644 index 0000000..cb2e15c --- /dev/null +++ b/export/data/snapshot/kaneton/machine/architecture/ia32/educational/include/pt.h @@ -0,0 +1,48 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/Down...chitecture/ia32/educational/include/pt.h + * + * created julien quintard [mon jan 10 09:31:37 2011] + * updated julien quintard [mon apr 11 13:21:04 2011] + */ + +#ifndef ARCHITECTURE_PT_H +#define ARCHITECTURE_PT_H 1 + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * defines the number entries composing a page table. + */ + +#define ARCHITECTURE_PT_SIZE 1024 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../pt.c + */ + +/* + * ../pt.c + */ + + +/* + * eop + */ + +#endif diff --git a/export/data/snapshot/kaneton/machine/architecture/ia32/educational/paging.c b/export/data/snapshot/kaneton/machine/architecture/ia32/educational/paging.c new file mode 100644 index 0000000..bf393bd --- /dev/null +++ b/export/data/snapshot/kaneton/machine/architecture/ia32/educational/paging.c @@ -0,0 +1,545 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...e/architecture/ia32/educational/paging.c + * + * created matthieu bucchianeri [tue dec 20 13:45:05 2005] + * updated julien quintard [tue apr 12 07:34:31 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file provides basic paging functionalities. + * + * note that the ia32/educational implementation makes use of the mirroring + * technique. this technique is used to prevent the kernel from looping + * infinitely whenever trying to map a page table for instance. indeed, + * whenever a page table must be mapped, a virtual memory area is picked. + * this memory area must be mapped by creating a mapping in the page + * directory/table hierarchical structure. in order to do so, the kernel page + * directory and the kernel page table referencing the given area must + * also be mapped. should not this be the case, the referencing page table, + * for example, should be mapped by creating a mapping etc. there is an + * obvious infinite loop occuring here where every page table which has + * to be mapped implies another page table to reference it, hence to be + * mapped as well. + * + * in order to prevent this infinite loop, the kernel relies on the + * mirroring technique. this mechanism consists in setting up a kernel + * page directory entry as acting as a special loopback. for example, the + * last page directory entry, i.e pd[1023], will not reference another page + * table as it is the case for the other page directory entries but the + * page directory itself. this way, the page directory, whenever the + * mirroring page directory entry is used, is considered by the microprocessor + * as page table and its entries as page tables entries. + * + * the following schema depicts this organization: + * + * pd @ 0x011e2000 + * pde 0 references 0x011e3000 + * pt @ 0x011e3000 + * pte 0 not present + * pte 1 references 0x00001000 + * pte 2 references 0x00002000 + * pte 3 references 0x00003000 + * pte 4 references 0x00004000 + * ... + * pde 2 not present + * pde 3 not present + * pde 4 references 0x011e4000 + * pt @ 0x011e4000 + * pte 0 references 0x01000000 + * pte 1 references 0x01001000 + * pte 2 references 0x01002000 + * ... + * pde 1023 references 0x011e2000 (the page directory itself) + * pt @ 0x011e2000 (the page directory acts as a page table) + * pte 0 references 0x011e3000 (the first page table: pde 0) + * pte 4 references 0x011e4000 (the second page table: pde 1) + * pte 1023 references 0x011e2000 (the page directory: pde 1023) + * + * this technique---assuming the kernel page directory is mapped, but this is + * quite obvious---enables the kernel to modify any page directory/table + * without mapping any intermediate page table. indeed, the kernel only needs + * to reference the page table it wishes to modify through its mirrored + * page directory entry. + * + * this technique implies that the last page directory entry is reserved + * for the mirroring mechanism. therefore, the last 4MB of virtual memory + * must be locked for that purpose meaning that 4MB of memory are wasted. + * + * finally, the reader should have understood that the mirroring mechanism + * only applies to the kernel tables. indeed, other address spaces' + * page directories and tables can be mapped normally since, should + * a kernel table need to be mapped to access another non-kernel table, it + * will be through the mirroring mechanism. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the kernel manager. + */ + +extern m_kernel _kernel; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function generates the CR3 register's content so that to be + * overwritten, hence referencing another page directory structure. + * + * note that the CR3 register is also referred to as the PDBR - Page Directory + * Base Register as it contains the address of the page directory in use. + * + * steps: + * + * 0) verify the arguments. + * 1) generate the CR3 register's content. + */ + +t_error architecture_paging_pdbr(t_paddr pd, + t_flags flags, + at_cr3* pdbr) +{ + /* + * 0) + */ + + if (pdbr == NULL) + MACHINE_ESCAPE("the 'pdbr' argument is null"); + + /* + * 1) + */ + + *pdbr = (pd & 0xfffff000) | flags; + + MACHINE_LEAVE(); +} + +/* + * this function maps a portion of the given segment to the given virtual + * address. + */ + +t_error architecture_paging_map(i_as id, + i_segment segment, + t_paddr offset, + t_options options, + t_vaddr address, + t_vsize size) +{ + /* FIXME[code to complete] */ + + MACHINE_LEAVE(); +} + +/* + * this function unmaps the mappings associated with the given address and + * size. + */ + +t_error architecture_paging_unmap(i_as id, + t_vaddr address, + t_vsize size) +{ + /* FIXME[code to complete] */ + + MACHINE_LEAVE(); +} + +/* + * this function reads data from a given segment by temporarily mapping + * the necessary pages. + * + * note that this function supports non-aligned addresses and sizes. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the segment object. + * 2) compute the aligned segment offset along with the non-aligned shift. + * 3) compute the last aligned page boundary. + * 4) reserve a region for the involved pages. + * 5) retrieve the region object. + * 6) copy data from the mapped region into the given buffer. + * 7) release the region. + */ + +t_error architecture_paging_read(i_segment id, + t_paddr offset, + void* buffer, + t_psize size) +{ + t_paddr shift; + i_region region; + t_paddr end; + o_segment* o; + o_region* r; + + /* + * 0) + */ + + if (buffer == NULL) + MACHINE_ESCAPE("the 'buffer' argument is null"); + + /* + * 1) + */ + + if (segment_get(id, &o) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the segment object"); + + /* + * 2) + */ + + if (offset % ___kaneton$pagesz) + { + shift = offset - (offset & ~(___kaneton$pagesz - 1)); + offset = offset & ~(___kaneton$pagesz - 1); + } + else + { + shift = 0; + } + + /* + * 3) + */ + + end = offset + shift + size; + + if (end % ___kaneton$pagesz) + end = (end & ~(___kaneton$pagesz - 1)) + ___kaneton$pagesz; + + /* + * 4) + */ + + if (region_reserve(_kernel.as, + id, + offset, + REGION_OPTION_NONE, + 0x0, + end - offset, + ®ion) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve a region"); + + /* + * 5) + */ + + if (region_get(_kernel.as, region, &r) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the region object"); + + /* + * 6) + */ + + memcpy(buffer, (void*)r->address + shift, size); + + /* + * 7) + */ + + if (region_release(_kernel.as, region) != ERROR_OK) + MACHINE_ESCAPE("unable to release the region"); + + MACHINE_LEAVE(); +} + +/* + * this function writes data to a given segment by temporarily mapping + * the necessary pages. + * + * note that this function supports non-aligned addresses and sizes. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the segment object. + * 2) compute the aligned segment offset along with the non-aligned shift. + * 3) compute the last aligned page boundary. + * 4) reserve a region for the involved pages. + * 5) retrieve the region object. + * 6) copy data from the given buffer to the mapped region. + * 7) release the region. + */ + +t_error architecture_paging_write(i_segment id, + t_paddr offset, + const void* buffer, + t_psize size) +{ + t_paddr shift; + i_region region; + t_paddr end; + o_segment* o; + o_region* r; + + /* + * 0) + */ + + if (buffer == NULL) + MACHINE_ESCAPE("the 'buffer' argument is null"); + + /* + * 1) + */ + + if (segment_get(id, &o) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the segment object"); + + /* + * 2) + */ + + if (offset % ___kaneton$pagesz) + { + shift = offset - (offset & ~(___kaneton$pagesz - 1)); + offset = offset & ~(___kaneton$pagesz - 1); + } + else + { + shift = 0; + } + + /* + * 3) + */ + + end = offset + shift + size; + + if (end % ___kaneton$pagesz) + end = (end & ~(___kaneton$pagesz - 1)) + ___kaneton$pagesz; + + /* + * 4) + */ + + if (region_reserve(_kernel.as, + id, + offset, + REGION_OPTION_NONE, + 0x0, + end - offset, + ®ion) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve a region"); + + /* + * 5) + */ + + if (region_get(_kernel.as, region, &r) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the region object"); + + /* + * 6) + */ + + memcpy((void*)r->address + shift, buffer, size); + + /* + * 7) + */ + + if (region_release(_kernel.as, region) != ERROR_OK) + MACHINE_ESCAPE("unable to release the region"); + + MACHINE_LEAVE(); +} + +/* + * this function copies data from a segment to another by temporarily + * mapping the necessary pages. + * + * note that this function supports non-aligned addresses and sizes. + * + * steps: + * + * 1) retrieve the segment objects. + * 2) compute the source aligned offset and non-aligned shift. + * 3) compute the source last aligned page boundary. + * 4) map the source pages through a region reservation. + * 5) retrieve the reserved region. + * 6) compute the destination aligned offset and non-aligned shift. + * 7) compute the destination last aligned page boundary. + * 8) map the destination pages through a region reservation. + * 9) retrieve the reserved region. + * 10) copy from the source mapped region to the destination mapped region. + * 11) release the reserved regions. + */ + +t_error architecture_paging_copy(i_region dst, + t_paddr to, + i_region src, + t_paddr from, + t_psize size) +{ + struct + { + struct + { + o_segment* object; + } segment; + struct + { + i_region id; + o_region* object; + } region; + t_paddr shift; + } source; + struct + { + struct + { + o_segment* object; + } segment; + struct + { + i_region id; + o_region* object; + } region; + t_paddr shift; + } destination; + t_paddr end; + + /* + * 1) + */ + + if (segment_get(dst, &destination.segment.object) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the segment object"); + + if (segment_get(src, &source.segment.object) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the segment object"); + + /* + * 2) + */ + + if (from % ___kaneton$pagesz) + { + source.shift = from - (from & ~(___kaneton$pagesz - 1)); + from = from & ~(___kaneton$pagesz - 1); + } + else + { + source.shift = 0; + } + + /* + * 3) + */ + + end = from + source.shift + size; + + if (end % ___kaneton$pagesz) + end = (end & ~(___kaneton$pagesz - 1)) + ___kaneton$pagesz; + + /* + * 4) + */ + + if (region_reserve(_kernel.as, + src, + from, + REGION_OPTION_NONE, + 0x0, + end - from, + &source.region.id) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve a region"); + + /* + * 5) + */ + + if (region_get(_kernel.as, + source.region.id, + &source.region.object) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the region object"); + + /* + * 6) + */ + + if (to % ___kaneton$pagesz) + { + destination.shift = to - (to & ~(___kaneton$pagesz - 1)); + to = to & ~(___kaneton$pagesz - 1); + } + else + { + destination.shift = 0; + } + + /* + * 7) + */ + + end = to + destination.shift + size; + + if (end % ___kaneton$pagesz) + end = (end & ~(___kaneton$pagesz - 1)) + ___kaneton$pagesz; + + /* + * 8) + */ + + if (region_reserve(_kernel.as, + dst, + to, + REGION_OPTION_NONE, + 0x0, + end - to, + &destination.region.id) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve a region"); + + /* + * 9) + */ + + if (region_get(_kernel.as, + destination.region.id, + &destination.region.object) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the region object"); + + /* + * 10) + */ + + memcpy((void*)destination.region.object->address + destination.shift, + (void*)source.region.object->address + source.shift, + size); + + /* + * 11) + */ + + if (region_release(_kernel.as, source.region.id) != ERROR_OK) + MACHINE_ESCAPE("unable to release the region"); + + if (region_release(_kernel.as, destination.region.id) != ERROR_OK) + MACHINE_ESCAPE("unable to release the region"); + + MACHINE_LEAVE(); +} diff --git a/export/data/snapshot/kaneton/machine/architecture/ia32/educational/pd.c b/export/data/snapshot/kaneton/machine/architecture/ia32/educational/pd.c new file mode 100644 index 0000000..bc86ceb --- /dev/null +++ b/export/data/snapshot/kaneton/machine/architecture/ia32/educational/pd.c @@ -0,0 +1,30 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...chine/architecture/ia32/educational/pd.c + * + * created matthieu bucchianeri [tue dec 20 19:56:20 2005] + * updated julien quintard [mon apr 11 13:45:04 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file contains functions for managing PDs - Page Directories. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* FIXME[complete if necessary] */ diff --git a/export/data/snapshot/kaneton/machine/architecture/ia32/educational/pt.c b/export/data/snapshot/kaneton/machine/architecture/ia32/educational/pt.c new file mode 100644 index 0000000..c8b2a60 --- /dev/null +++ b/export/data/snapshot/kaneton/machine/architecture/ia32/educational/pt.c @@ -0,0 +1,32 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...chine/architecture/ia32/educational/pt.c + * + * created matthieu bucchianeri [tue dec 20 19:56:48 2005] + * updated julien quintard [mon apr 11 13:45:11 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file contains functions for manipulating PTs - Page Tables. + * + * note that the whole file is extremely similar to pd.c. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* FIXME[complete if necessary] */ diff --git a/export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/as.c b/export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/as.c new file mode 100644 index 0000000..2bbe576 --- /dev/null +++ b/export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/as.c @@ -0,0 +1,88 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...achine/glue/ibm-pc.ia32/educational/as.c + * + * created matthieu bucchianeri [sat jun 16 18:10:38 2007] + * updated julien quintard [sat feb 5 13:57:19 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file implements the glue for the address space manager. + * + * the glue particularly sets up the initial address space's state by + * building the IA32 page directory. note that glue_as_reserve() + * detects the kernel address space as this one must be prepared specially. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the kernel manager. + */ + +extern m_kernel _kernel; + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the address space dispatcher. + */ + +d_as glue_as_dispatch = + { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + glue_as_reserve, + NULL, + NULL, + NULL + }; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function initializes the given address space depending to the task, + * being either the kernel or one of the servers i.e a driver, service or + * guest. + */ + +t_error glue_as_reserve(i_task task, + i_as* as) +{ + if (*as == _kernel.as) + { + if (architecture_environment_kernel(*as) != ERROR_OK) + MACHINE_ESCAPE("unable to initialize the kernel's address space"); + } + else + { + if (architecture_environment_server(*as) != ERROR_OK) + MACHINE_ESCAPE("unable to initialize the server's address space"); + } + + MACHINE_LEAVE(); +} diff --git a/export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/event.c b/export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/event.c new file mode 100644 index 0000000..0d7e027 --- /dev/null +++ b/export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/event.c @@ -0,0 +1,45 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...ine/glue/ibm-pc.ia32/educational/event.c + * + * created renaud voltz [mon feb 13 01:05:52 2006] + * updated julien quintard [sat feb 5 12:30:16 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file implements the event manager's glue. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the event dispatcher. + */ + +d_event glue_event_dispatch = + { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL + }; diff --git a/export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/include/as.h b/export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/include/as.h new file mode 100644 index 0000000..7d89073 --- /dev/null +++ b/export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/include/as.h @@ -0,0 +1,90 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...lue/ibm-pc.ia32/educational/include/as.h + * + * created julien quintard [sun jun 3 23:54:56 2007] + * updated julien quintard [sat feb 5 15:44:46 2011] + */ + +#ifndef GLUE_AS_H +#define GLUE_AS_H 1 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function defines the address space dispatcher. + */ + +#define machine_include_as() \ + extern d_as glue_as_dispatch + +/* + * this macro-function dispatches the address space calls. + */ + +#define machine_call_as(_function_, _args_...) \ + ( \ + { \ + t_error _r_ = ERROR_OK; \ + \ + if (glue_as_dispatch.as_ ## _function_ != NULL) \ + _r_ = glue_as_dispatch.as_ ## _function_(_args_); \ + \ + _r_; \ + } \ + ) + +/* + * this macro-function includes data in the 'm_as' type. + */ + +#define machine_data_m_as() + +/* + * this macro-function includes data in the address space object 'o_as': + * the address space IA32-specific PD - Page directory's location. + */ + +#define machine_data_o_as() \ + struct \ + { \ + t_paddr pd; \ + } machine; + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../as.c + */ + +/* + * ../as.c + */ + +t_error glue_as_show(i_as id, + mt_margin margin); + +t_error glue_as_dump(void); + +t_error glue_as_reserve(i_task task, + i_as* as); + + +/* + * eop + */ + +#endif diff --git a/export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/include/event.h b/export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/include/event.h new file mode 100644 index 0000000..c5a47db --- /dev/null +++ b/export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/include/event.h @@ -0,0 +1,83 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane.../ibm-pc.ia32/educational/include/event.h + * + * created julien quintard [wed jun 6 16:15:26 2007] + * updated julien quintard [sat feb 5 13:30:56 2011] + */ + +#ifndef GLUE_EVENT_H +#define GLUE_EVENT_H 1 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function defines the event dispatcher. + */ + +#define machine_include_event() \ + extern d_event glue_event_dispatch + +/* + * this macro-function dispatches the CPU calls. + */ + +#define machine_call_event(_function_, _args_...) \ + ( \ + { \ + t_error _r_ = ERROR_OK; \ + \ + if (glue_event_dispatch.event_ ## _function_ != NULL) \ + _r_ = glue_event_dispatch.event_ ## _function_(_args_); \ + \ + _r_; \ + } \ + ) + +/* + * this macro-function includes data in 'm_event'. + */ + +#define machine_data_m_event() + +/* + * this macro-function includes data in 'o_event'. + */ + +#define machine_data_o_event() + +/* + * this macro-function includes data in 'o_event_message'. + */ + +#define machine_data_o_event_message() + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../event.c + */ + +/* + * ../event.c + */ + + +/* + * eop + */ + +#endif diff --git a/export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/region.c b/export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/region.c new file mode 100644 index 0000000..e7c167a --- /dev/null +++ b/export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/region.c @@ -0,0 +1,49 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...ne/glue/ibm-pc.ia32/educational/region.c + * + * created julien quintard [wed dec 14 07:06:44 2005] + * updated julien quintard [sat feb 5 14:37:43 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file implements the region manager's glue which basically consists + * in updating the address space's page directory, tables etc. in order + * to reflect the core operation. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the region dispatcher. + */ + +d_region glue_region_dispatch = + { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL + }; diff --git a/export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/scheduler.c b/export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/scheduler.c new file mode 100644 index 0000000..3296be6 --- /dev/null +++ b/export/data/snapshot/kaneton/machine/glue/ibm-pc.ia32/educational/scheduler.c @@ -0,0 +1,219 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...glue/ibm-pc.ia32/educational/scheduler.c + * + * created matthieu bucchianeri [sat jun 3 22:45:19 2006] + * updated julien quintard [tue feb 15 21:03:52 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file implements the scheduler manager's glue. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the scheduler manager. + */ + +extern m_scheduler _scheduler; + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the scheduler dispatcher. + */ + +d_scheduler glue_scheduler_dispatch = + { + NULL, + glue_scheduler_dump, + glue_scheduler_start, + glue_scheduler_stop, + glue_scheduler_quantum, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL + }; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function dumps the scheduler manager's machine part. + */ + +t_error glue_scheduler_dump(void) +{ + module_call(console, message, + '#', + " machine: timer(%qd)\n", + _scheduler.machine.timer); + + MACHINE_LEAVE(); +} + +/* + * this function starts the scheduler. + * + * steps: + * + * 1) reserve a timer so that a context switch is triggered every quantum + * milliseconds. + */ + +t_error glue_scheduler_start(i_cpu cpu) +{ + /* + * 1) + */ + + if (timer_reserve(TIMER_TYPE_FUNCTION, + TIMER_ROUTINE(glue_scheduler_switch), + TIMER_DATA(NULL), + _scheduler.quantum, + TIMER_OPTION_REPEAT, + &_scheduler.machine.timer) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve the timer"); + + MACHINE_LEAVE(); +} + +/* + * this function contributes to stopping the scheduler by manually triggering + * the timer interrupt in order to induce an immediate scheduler election. + * + * during this election, the scheduler will notice the scheduler's state + * has changed to stop and will do the necessary to stop electing threads. + * + * 1) retrieve the current CPU's scheduler. + * 2) if the CPU to stop is the current one, yield the execution in order + * to induce a scheduler election. + * 3) release the scheduler timer. + */ + +t_error glue_scheduler_stop(i_cpu cpu) +{ + o_scheduler* scheduler; + + /* + * 1) + */ + + if (scheduler_current(&scheduler) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the current scheduler"); + + /* + * 2) + */ + + if (scheduler->cpu == cpu) + { + if (scheduler_yield() != ERROR_OK) + MACHINE_ESCAPE("unable to yield the execution"); + } + + /* + * 3) + */ + + if (timer_release(_scheduler.machine.timer) != ERROR_OK) + MACHINE_ESCAPE("unable to release the timer"); + + MACHINE_LEAVE(); +} + +/* + * this handler is triggered whenever the scheduler quantum of time has + * elapsed. this function requests the scheduler to elect a new thread + * and performs the context switch. + * + * steps: + * + * 1) retrieve the current CPU's scheduler. + * 2) save the identifier of the currently running thread. + * 3) request a thread election to the scheduler. + * 4) save the identifier of the thread about to be executed. + * 5) perform a context switch between the two threads. + */ + +void glue_scheduler_switch(void) +{ + o_scheduler* scheduler; + i_thread current; + i_thread future; + + /* + * 1) + */ + + assert(scheduler_current(&scheduler) == ERROR_OK); + + /* + * 2) + */ + + current = scheduler->thread; + + /* + * 3) + */ + + assert(scheduler_elect() == ERROR_OK); + + /* + * 4) + */ + + future = scheduler->thread; + + /* + * 5) + */ + + assert(architecture_context_switch(current, future) == ERROR_OK); +} + +/* + * this function sets the scheduler quantum value. + * + * steps: + * + * 1) note that since the quantum has been updated, the scheduler timer + * must also be adujsted. + */ + +t_error glue_scheduler_quantum(t_quantum quantum) +{ + /* + * 1) + */ + + if (timer_update(_scheduler.machine.timer, quantum) != ERROR_OK) + MACHINE_ESCAPE("unable to adjust the timer's delay to the " + "scheduler's quantum"); + + MACHINE_LEAVE(); +} diff --git a/export/data/snapshot/test/Makefile b/export/data/snapshot/test/Makefile new file mode 100644 index 0000000..c55e14b --- /dev/null +++ b/export/data/snapshot/test/Makefile @@ -0,0 +1,60 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/export/output/kaneton/test/Makefile +# +# created julien quintard [fri jun 29 11:19:40 2007] +# updated julien quintard [sat feb 5 12:10:10 2011] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := test + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers + +# +# ---------- variables -------------------------------------------------------- +# + +SUBDIRS := client \ + packages + +# +# ---------- rules ------------------------------------------------------------ +# + +main: + +clear: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,clear,) ; \ + done + + $(call env_purge,) + +prototypes: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,clear,) ; \ + done + +headers: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,clear,) ; \ + done diff --git a/export/data/snapshot/tool/Makefile b/export/data/snapshot/tool/Makefile new file mode 100644 index 0000000..f6473a6 --- /dev/null +++ b/export/data/snapshot/tool/Makefile @@ -0,0 +1,50 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/export/output/kaneton/tool/Makefile +# +# created julien quintard [fri nov 28 16:24:37 2008] +# updated julien quintard [sat feb 5 12:11:38 2011] +# + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers + +# +# ---------- variables -------------------------------------------------------- +# + +SUBDIRS := + +# +# ---------- rules ------------------------------------------------------------ +# + +main: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,,) ; \ + done + +clear: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,clear,) ; \ + done + + $(call env_purge,) + +prototypes: + +headers: diff --git a/export/export.py b/export/export.py new file mode 100644 index 0000000..5e9e12c --- /dev/null +++ b/export/export.py @@ -0,0 +1,137 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/export/export.py +# +# created francois goudal [sat oct 25 20:57:38 2008] +# updated julien quintard [sat feb 5 11:43:43 2011] +# + +# +# ---------- imports ---------------------------------------------------------- +# + +import sys +import yaml +import env + +# +# ---------- globals ---------------------------------------------------------- +# + +g_verbose = 1 +g_modules = {} +g_modules_parameters = {} +g_action_data = [] +g_export_dir = "" + +# +# ---------- functions -------------------------------------------------------- +# + +# +# modules_load() +# +# dynamically loads action modules from the "modules" directory. +# +def modules_load(): + global g_modules + + env.display(env.HEADER_OK, 'loading action modules', env.OPTION_NONE) + if (env.path(sys.argv[0], env.OPTION_DIRECTORY) == ""): + moduledir = './modules' + else: + moduledir = env.path(sys.argv[0], env.OPTION_DIRECTORY) + '/modules' + sys.path.append(moduledir) + for i in env.list(moduledir, env.OPTION_FILE): + if i.endswith('.py'): + mod = __import__(i[:-3]) + try: + init_str = mod.module_init() + except AttributeError: + sys.stderr.write('error: The module file ' + i + + ' is missing the module_init function\n') + sys.exit(1) + g_modules[init_str[0]] = mod + g_modules_parameters[init_str[0]] = init_str + env.display(env.HEADER_OK, " loaded module " + init_str[0], env.OPTION_NONE) + +# +# action_data_load() +# +# loads the action data from the YAML file +# +def action_data_load(filename): + env.display(env.HEADER_OK, 'loading action data description file ' + filename, env.OPTION_NONE) + stream = file(env._EXPORT_DIR_ + '/' + filename + '.yml', 'r') + return yaml.load(stream) + + + +def parse_data_file(filename): + global g_action_data + global g_modules + global g_export_dir + global g_modules_parameters + + g_action_data = action_data_load(filename) + for i in g_action_data: + if i['operation'] == 'import': + env.display(env.HEADER_OK, 'action import ' + i['filename'], env.OPTION_NONE) + parse_data_file("behaviours/" + i['filename']) + else: + arg = {} + for j in range(1, len(g_modules_parameters[i['operation']])): + arg[g_modules_parameters[i['operation']][j]] = i[g_modules_parameters[ + i['operation']][j]]; + if g_modules[i['operation']].module_action(g_export_dir, arg) != 0: + sys.exit(1) + + +# +# main() +# +# main function: goes through the actions and calls the modules accordingly +# +def main(): + global g_modules + global g_action_data + global g_export_dir + + if (len(sys.argv) != 2): + env.display(env.HEADER_ERROR, "usage: export.py [behaviour]", + env.OPTION_NONE) + env.display(env.HEADER_NONE, "", + env.OPTION_NONE) + env.display(env.HEADER_ERROR, "behaviours:", + env.OPTION_NONE) + for i in env.list(env._EXPORT_DIR_ + "/behaviours", env.OPTION_FILE): + env.display(env.HEADER_ERROR, " " + i[:-4], env.OPTION_NONE) + return 1 + + modules_load() + + work_dir = env.temporary(env.OPTION_DIRECTORY) + + g_export_dir = work_dir + "/kaneton" + + env.display(env.HEADER_OK, 'exporting to ' + g_export_dir, env.OPTION_NONE) + + env.display(env.HEADER_OK, 'running export actions', env.OPTION_NONE) + + parse_data_file("behaviours/" + sys.argv[1]) + + env.display(env.HEADER_OK, 'cleaning temporary directory', env.OPTION_NONE) + env.remove(work_dir, env.OPTION_NONE) + + env.display(env.HEADER_OK, 'export finished', env.OPTION_NONE) + +# +# ---------- entry point ------------------------------------------------------ +# + +main() diff --git a/export/modules/Makefile b/export/modules/Makefile new file mode 100644 index 0000000..24294f5 --- /dev/null +++ b/export/modules/Makefile @@ -0,0 +1,37 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/export/Makefile +# +# created julien quintard [sun jun 10 14:53:00 2007] +# updated julien quintard [mon jun 11 19:29:21 2007] +# + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers + +# +# ---------- rules ------------------------------------------------------------ +# + +main: + +clear: + $(call env_purge) + +prototypes: + +headers: diff --git a/export/modules/bremove.py b/export/modules/bremove.py new file mode 100644 index 0000000..38923c7 --- /dev/null +++ b/export/modules/bremove.py @@ -0,0 +1,72 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/export/modules/bremove.py +# +# created francois goudal [sat oct 25 20:57:38 2008] +# updated julien quintard [sat feb 5 10:33:21 2011] +# + +# +# ---------- imports ---------------------------------------------------------- +# + +import env +import re + +# +# ---------- functions -------------------------------------------------------- +# + +# +# module_init() +# +# called by export.py on startup, to discover the available actions +# returns an array of strings, the index 0 is the action name, and the +# others are the parameters required in the YAML file. +# +def module_init(): + return ['bremove','id'] + +# +# module_action(export_dir, arg) +# +# called by export.py when required by the YAML file. +# this function does the job of the action. +# arg contains the parameters required as specified in the module_init return +# value. +# +def module_action(export_dir, arg): + strip = 0 + env.display(env.HEADER_OK, 'action bremove ' + arg['id'], env.OPTION_NONE) + id = arg['id'].split('::', 1) + filepath = id[0] + label = id[1] + tmpfilepath = env.temporary(env.OPTION_FILE) + srcf = open(export_dir + '/' + filepath, 'r') + dstf = open(tmpfilepath, 'w') + line = srcf.readline() + startpattern = re.compile(".*[[]block\:\:" + label + "[]].*") + endpattern = re.compile(".*[[]/block\:\:" + label + "[]].*") + while line != "": + if strip == 0: + if startpattern.match(line) != None: + strip = 1 + if strip != 1: + dstf.write(line) + if strip == 1: + if endpattern.match(line) != None: + strip = 2 + line = srcf.readline() + dstf.close() + srcf.close() + env.copy(tmpfilepath, export_dir + '/' + filepath, env.OPTION_NONE) + env.remove(tmpfilepath, env.OPTION_NONE) + if strip == 0: + env.display(env.HEADER_ERROR, 'no block ' + label + ' in file ' + filepath, env.OPTION_NONE) + return -1 + return 0 diff --git a/export/modules/breplace.py b/export/modules/breplace.py new file mode 100644 index 0000000..4422646 --- /dev/null +++ b/export/modules/breplace.py @@ -0,0 +1,73 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/export/modules/breplace.py +# +# created francois goudal [sat oct 25 20:57:38 2008] +# updated julien quintard [sat feb 5 10:33:33 2011] +# + +# +# ---------- imports ---------------------------------------------------------- +# + +import env +import re + +# +# ---------- functions -------------------------------------------------------- +# + +# +# module_init() +# +# called by export.py on startup, to discover the available actions +# returns an array of strings, the index 0 is the action name, and the +# others are the parameters required in the YAML file. +# +def module_init(): + return ['breplace','id','data'] + +# +# module_action(export_dir, arg) +# +# called by export.py when required by the YAML file. +# this function does the job of the action. +# arg contains the parameters required as specified in the module_init return +# value. +# +def module_action(export_dir, arg): + strip = 0 + env.display(env.HEADER_OK, 'action breplace ' + arg['id'], env.OPTION_NONE) + id = arg['id'].split('::', 1) + filepath = id[0] + label = id[1] + tmpfilepath = env.temporary(env.OPTION_FILE) + srcf = open(export_dir + '/' + filepath, 'r') + dstf = open(tmpfilepath, 'w') + line = srcf.readline() + startpattern = re.compile(".*[[]block\:\:" + label + "[]].*") + endpattern = re.compile(".*[[]/block\:\:" + label + "[]].*") + while line != "": + if strip == 0: + if startpattern.match(line) != None: + strip = 1 + dstf.write(arg['data']) + if strip != 1: + dstf.write(line) + if strip == 1: + if endpattern.match(line) != None: + strip = 2 + line = srcf.readline() + dstf.close() + srcf.close() + env.copy(tmpfilepath, export_dir + '/' + filepath, env.OPTION_NONE) + env.remove(tmpfilepath, env.OPTION_NONE) + if strip == 0: + env.display(env.HEADER_ERROR, 'no block ' + label + ' in file ' + filepath, env.OPTION_NONE) + return -1 + return 0 diff --git a/export/modules/fmove.py b/export/modules/fmove.py new file mode 100644 index 0000000..d68be9c --- /dev/null +++ b/export/modules/fmove.py @@ -0,0 +1,45 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /Users/francois/kaneton/tool/export/modules/fmove.py +# +# created francois goudal [sat oct 25 20:57:38 2008] +# updated francois goudal [wed nov 12 05:26:29 2008] +# + +# +# ---------- imports ---------------------------------------------------------- +# + +import env + +# +# ---------- functions -------------------------------------------------------- +# + +# +# module_init() +# +# called by export.py on startup, to discover the available actions +# returns an array of strings, the index 0 is the action name, and the +# others are the parameters required in the YAML file. +# +def module_init(): + return ['fmove','src','dst'] + +# +# module_action(export_dir, arg) +# +# called by export.py when required by the YAML file. +# this function does the job of the action. +# arg contains the parameters required as specified in the module_init return +# value. +# +def module_action(export_dir, arg): + env.display(env.HEADER_OK, 'action fmove ' + arg['src'] + ' to ' + arg['dst'], env.OPTION_NONE) + env.move(export_dir + '/' + arg['src'], export_dir + '/' + arg['dst'], env.OPTION_NONE) + return 0 diff --git a/export/modules/fremove.py b/export/modules/fremove.py new file mode 100644 index 0000000..5ef72ca --- /dev/null +++ b/export/modules/fremove.py @@ -0,0 +1,45 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /Users/francois/kaneton/tool/export/modules/remove.py +# +# created francois goudal [sat oct 25 20:57:38 2008] +# updated francois goudal [wed nov 12 05:26:29 2008] +# + +# +# ---------- imports ---------------------------------------------------------- +# + +import env + +# +# ---------- functions -------------------------------------------------------- +# + +# +# module_init() +# +# called by export.py on startup, to discover the available actions +# returns an array of strings, the index 0 is the action name, and the +# others are the parameters required in the YAML file. +# +def module_init(): + return ['fremove','path'] + +# +# module_action(export_dir, arg) +# +# called by export.py when required by the YAML file. +# this function does the job of the action. +# arg contains the parameters required as specified in the module_init return +# value. +# +def module_action(export_dir, arg): + env.display(env.HEADER_OK, 'action fremove ' + arg['path'], env.OPTION_NONE) + env.remove(export_dir + '/' + arg['path'], env.OPTION_NONE) + return 0 diff --git a/export/modules/fremovepattern.py b/export/modules/fremovepattern.py new file mode 100644 index 0000000..ddf958c --- /dev/null +++ b/export/modules/fremovepattern.py @@ -0,0 +1,46 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /Users/francois/kaneton/tool/export/modules/remove.py +# +# created francois goudal [sat oct 25 20:57:38 2008] +# updated francois goudal [wed nov 12 05:26:29 2008] +# + +# +# ---------- imports ---------------------------------------------------------- +# + +import env + +# +# ---------- functions -------------------------------------------------------- +# + +# +# module_init() +# +# called by export.py on startup, to discover the available actions +# returns an array of strings, the index 0 is the action name, and the +# others are the parameters required in the YAML file. +# +def module_init(): + return ['fremovepattern','pattern'] + +# +# module_action(export_dir, arg) +# +# called by export.py when required by the YAML file. +# this function does the job of the action. +# arg contains the parameters required as specified in the module_init return +# value. +# +def module_action(export_dir, arg): + env.display(env.HEADER_OK, 'action fremovepattern ' + arg['pattern'], env.OPTION_NONE) + for d in env.search(export_dir, arg['pattern'], env.OPTION_RECURSIVE | env.OPTION_DIRECTORY | env.OPTION_FILE): + env.remove(d, env.OPTION_NONE) + return 0 diff --git a/export/modules/freplace.py b/export/modules/freplace.py new file mode 100644 index 0000000..71e5e69 --- /dev/null +++ b/export/modules/freplace.py @@ -0,0 +1,45 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/export/modules/freplace.py +# +# created francois goudal [sat oct 25 20:57:38 2008] +# updated julien quintard [sat feb 5 11:32:56 2011] +# + +# +# ---------- imports ---------------------------------------------------------- +# + +import env + +# +# ---------- functions -------------------------------------------------------- +# + +# +# module_init() +# +# called by export.py on startup, to discover the available actions +# returns an array of strings, the index 0 is the action name, and the +# others are the parameters required in the YAML file. +# +def module_init(): + return ['freplace','src','dst'] + +# +# module_action(export_dir, arg) +# +# called by export.py when required by the YAML file. +# this function does the job of the action. +# arg contains the parameters required as specified in the module_init return +# value. +# +def module_action(export_dir, arg): + env.display(env.HEADER_OK, 'action freplace ' + arg['src'] + ' to ' + arg['dst'], env.OPTION_NONE) + env.copy(export_dir + '/' + arg['src'], export_dir + '/' + arg['dst'], env.OPTION_NONE) + return 0 diff --git a/export/modules/initenv.py b/export/modules/initenv.py new file mode 100644 index 0000000..f6a47a7 --- /dev/null +++ b/export/modules/initenv.py @@ -0,0 +1,46 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/export/modules/initenv.py +# +# created francois goudal [sat oct 25 20:57:38 2008] +# updated julien quintard [mon apr 20 15:03:29 2009] +# + +# +# ---------- imports ---------------------------------------------------------- +# + +import env + +# +# ---------- functions -------------------------------------------------------- +# + +# +# module_init() +# +# called by export.py on startup, to discover the available actions +# returns an array of strings, the index 0 is the action name, and the +# others are the parameters required in the YAML file. +# +def module_init(): + return ['initenv'] + +# +# module_action(export_dir, arg) +# +# called by export.py when required by the YAML file. +# this function does the job of the action. +# arg contains the parameters required as specified in the module_init return +# value. +# +def module_action(export_dir, arg): + env.display(env.HEADER_OK, 'action initenv', env.OPTION_NONE) + env.launch(export_dir + '/environment/initialize.py', "", env.OPTION_QUIET) + env.launch(export_dir + '/Makefile', "clean", env.OPTION_QUIET) + return 0 diff --git a/export/modules/localexport.py b/export/modules/localexport.py new file mode 100644 index 0000000..9fe9fd7 --- /dev/null +++ b/export/modules/localexport.py @@ -0,0 +1,46 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /Users/francois/kaneton/tool/export/modules/remove.py +# +# created francois goudal [sat oct 25 20:57:38 2008] +# updated francois goudal [wed nov 12 05:26:29 2008] +# + +# +# ---------- imports ---------------------------------------------------------- +# + +import env + +# +# ---------- functions -------------------------------------------------------- +# + +# +# module_init() +# +# called by export.py on startup, to discover the available actions +# returns an array of strings, the index 0 is the action name, and the +# others are the parameters required in the YAML file. +# +def module_init(): + return ['localexport'] + +# +# module_action(export_dir, arg) +# +# called by export.py when required by the YAML file. +# this function does the job of the action. +# arg contains the parameters required as specified in the module_init return +# value. +# +def module_action(export_dir, arg): + env.display(env.HEADER_OK, 'action localexport', env.OPTION_NONE) + env.copy(env._SOURCE_DIR_, export_dir, env.OPTION_NONE) + + return 0 diff --git a/export/modules/mkdir.py b/export/modules/mkdir.py new file mode 100644 index 0000000..4e4d4ef --- /dev/null +++ b/export/modules/mkdir.py @@ -0,0 +1,45 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/export/modules/mkdir.py +# +# created francois goudal [sat oct 25 20:57:38 2008] +# updated julien quintard [sat feb 5 11:36:28 2011] +# + +# +# ---------- imports ---------------------------------------------------------- +# + +import env + +# +# ---------- functions -------------------------------------------------------- +# + +# +# module_init() +# +# called by export.py on startup, to discover the available actions +# returns an array of strings, the index 0 is the action name, and the +# others are the parameters required in the YAML file. +# +def module_init(): + return ['mkdir','path'] + +# +# module_action(export_dir, arg) +# +# called by export.py when required by the YAML file. +# this function does the job of the action. +# arg contains the parameters required as specified in the module_init return +# value. +# +def module_action(export_dir, arg): + env.display(env.HEADER_OK, 'action mkdir ' + arg['path'], env.OPTION_NONE) + env.mkdir(export_dir + '/' + arg['path'], env.OPTION_NONE) + return 0 diff --git a/export/modules/svnexport.py b/export/modules/svnexport.py new file mode 100644 index 0000000..df08633 --- /dev/null +++ b/export/modules/svnexport.py @@ -0,0 +1,46 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/export/modules/svnexport.py +# +# created francois goudal [sat oct 25 20:57:38 2008] +# updated julien quintard [sat feb 5 11:14:04 2011] +# + +# +# ---------- imports ---------------------------------------------------------- +# + +import env + +# +# ---------- functions -------------------------------------------------------- +# + +# +# module_init() +# +# called by export.py on startup, to discover the available actions +# returns an array of strings, the index 0 is the action name, and the +# others are the parameters required in the YAML file. +# +def module_init(): + return ['svnexport'] + +# +# module_action(export_dir, arg) +# +# called by export.py when required by the YAML file. +# this function does the job of the action. +# arg contains the parameters required as specified in the module_init return +# value. +# +def module_action(export_dir, arg): + env.display(env.HEADER_OK, 'action svnexport', env.OPTION_NONE) + env.launch("svn", "export svn+ssh://subversion@repositories.passeism.org/kaneton " + export_dir,env.OPTION_NONE) + + return 0 diff --git a/export/modules/symlink.py b/export/modules/symlink.py new file mode 100644 index 0000000..079b4e2 --- /dev/null +++ b/export/modules/symlink.py @@ -0,0 +1,45 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /Users/francois/kaneton/tool/export/modules/symlink.py +# +# created francois goudal [sat oct 25 20:57:38 2008] +# updated francois goudal [wed nov 12 05:26:29 2008] +# + +# +# ---------- imports ---------------------------------------------------------- +# + +import env + +# +# ---------- functions -------------------------------------------------------- +# + +# +# module_init() +# +# called by export.py on startup, to discover the available actions +# returns an array of strings, the index 0 is the action name, and the +# others are the parameters required in the YAML file. +# +def module_init(): + return ['symlink','name','target'] + +# +# module_action(export_dir, arg) +# +# called by export.py when required by the YAML file. +# this function does the job of the action. +# arg contains the parameters required as specified in the module_init return +# value. +# +def module_action(export_dir, arg): + env.display(env.HEADER_OK, 'action symlink ' + arg['name'] + ' to ' + arg['target'], env.OPTION_NONE) + env.link(export_dir + '/' + arg['name'], arg['target'], env.OPTION_NONE) + return 0 diff --git a/export/modules/tarball.py b/export/modules/tarball.py new file mode 100644 index 0000000..2a73aea --- /dev/null +++ b/export/modules/tarball.py @@ -0,0 +1,49 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/export/modules/tarball.py +# +# created francois goudal [sat oct 25 20:57:38 2008] +# updated julien quintard [sat feb 5 11:42:05 2011] +# + +# +# ---------- imports ---------------------------------------------------------- +# + +import env + +# +# ---------- functions -------------------------------------------------------- +# + +# +# module_init() +# +# called by export.py on startup, to discover the available actions +# returns an array of strings, the index 0 is the action name, and the +# others are the parameters required in the YAML file. +# +def module_init(): + return ['tarball','filename'] + +# +# module_action(export_dir, arg) +# +# called by export.py when required by the YAML file. +# this function does the job of the action. +# arg contains the parameters required as specified in the module_init return +# value. +# +def module_action(export_dir, arg): + env.display(env.HEADER_OK, 'action tarball ' + arg['filename'], env.OPTION_NONE) + directory = env.cwd(env.OPTION_NONE) + env.cd(env.path(export_dir, env.OPTION_DIRECTORY), env.OPTION_NONE) + env.remove(env._EXPORT_DIR_ + "/output/" + arg['filename'] + ".tar.bz2", env.OPTION_NONE) + env.pack("kaneton", env._EXPORT_DIR_ + "/output/" + arg['filename'] + ".tar.bz2", env.OPTION_NONE) + env.cd(directory, env.OPTION_NONE) + return 0 diff --git a/kaneton/Makefile b/kaneton/Makefile new file mode 100644 index 0000000..8738dba --- /dev/null +++ b/kaneton/Makefile @@ -0,0 +1,83 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/kaneton/Makefile +# +# created julien quintard [sun jun 10 14:54:43 2007] +# updated julien quintard [sun jan 9 21:42:48 2011] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +SUBDIRS := core \ + machine \ + library \ + modules + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(_KANETON_) + +$(_KANETON_): $(_CORE_LO_) $(_MACHINE_LO_) \ + $(_LIBRARY_LO_) $(_MODULES_LO_) \ + $(_KERNEL_LAYOUT_) + $(call env_remove,$(_KANETON_),) + + $(call env_executable,$(_KANETON_), \ + $(_CORE_LO_) $(_MACHINE_LO_) \ + $(_LIBRARY_LO_) $(_MODULES_LO_), \ + $(_KERNEL_LAYOUT_), \ + $(ENV_OPTION_NO_STANDARD)) + +clear: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,clear,) ; \ + done + + $(call env_remove,$(_KANETON_),) + + $(call env_purge,) + +prototypes: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,prototypes,) ; \ + done + +headers: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,headers,) ; \ + done + +dependencies: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,,) ; \ + done + +endif diff --git a/kaneton/core/Makefile b/kaneton/core/Makefile new file mode 100644 index 0000000..41b8279 --- /dev/null +++ b/kaneton/core/Makefile @@ -0,0 +1,148 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/kaneton/core/Makefile +# +# created julien quintard [tue jun 26 15:39:11 2007] +# updated julien quintard [wed mar 2 23:04:20 2011] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/core + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +SUBDIRS := as \ + capability \ + clock \ + cpu \ + event \ + id \ + interface \ + io \ + kernel \ + map \ + message \ + region \ + scheduler \ + segment \ + set \ + task \ + thread \ + timer + +# kaneton objects + +KANETON_LO := $(_AS_LO_) \ + $(_CAPABILITY_LO_) \ + $(_CLOCK_LO_) \ + $(_CPU_LO_) \ + $(_DEBUG_LO_) \ + $(_EVENT_LO_) \ + $(_ID_LO_) \ + $(_INTERFACE_LO_) \ + $(_IO_LO_) \ + $(_KERNEL_LO_) \ + $(_MAP_LO_) \ + $(_MESSAGE_LO_) \ + $(_REGION_LO_) \ + $(_SCHEDULER_LO_) \ + $(_SEGMENT_LO_) \ + $(_SET_LO_) \ + $(_TASK_LO_) \ + $(_THREAD_LO_) \ + $(_TIMER_LO_) + +# core component + +CORE_INCLUDE := $(_CORE_INCLUDE_DIR_)/core.h + +CORE_C := core.c + +CORE_O := $(CORE_C:.c=.o) + +# version file + +VERSION_C := version.c + +VERSION_O := $(VERSION_C:.c=.o) + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(_CORE_LO_) + +$(_CORE_LO_): $(CORE_O) $(KANETON_LO) + $(call env_remove,$(_CORE_LO_),) + + $(call env_version,$(VERSION_C)) + $(call env_compile-c,$(VERSION_O),$(VERSION_C),) + + $(call env_archive,$(_CORE_LO_),$(CORE_O) \ + $(VERSION_O) \ + $(KANETON_LO),) + +clear: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,clear,) ; \ + done + + $(call env_remove,$(VERSION_O),) + $(call env_remove,$(VERSION_C),) + + $(call env_remove,$(CORE_O),) + + $(call env_remove,$(_CORE_LO_),) + + $(call env_purge,) + +prototypes: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,prototypes,) ; \ + done + + $(call env_prototypes,$(CORE_INCLUDE),) + +headers: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,headers,) ; \ + done + + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(CORE_C),) + +dependencies: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,,) ; \ + done + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/core/as/Makefile b/kaneton/core/as/Makefile new file mode 100644 index 0000000..952aa0f --- /dev/null +++ b/kaneton/core/as/Makefile @@ -0,0 +1,76 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/kaneton/core/as/Makefile +# +# created julien quintard [sun jun 10 15:47:23 2007] +# updated julien quintard [wed apr 15 07:00:46 2009] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/core/as + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +AS_C := as.c + +AS_O := $(AS_C:.c=.o) + +AS_INCLUDE := $(_CORE_INCLUDE_DIR_)/as.h + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(_AS_LO_) + +$(_AS_LO_): $(AS_O) + $(call env_remove,$(_AS_LO_),) + + $(call env_archive,$(_AS_LO_),$(AS_O),) + +clear: + $(call env_remove,$(AS_O),) + + $(call env_remove,$(_AS_LO_),) + + $(call env_purge,) + +prototypes: + $(call env_prototypes,$(AS_INCLUDE),) + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(AS_C),) + +dependencies: + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/core/as/as.c b/kaneton/core/as/as.c new file mode 100644 index 0000000..2e757d8 --- /dev/null +++ b/kaneton/core/as/as.c @@ -0,0 +1,1364 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/as/as.c + * + * created julien quintard [tue dec 13 03:05:27 2005] + * updated julien quintard [mon jan 31 13:58:09 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * the address space manager provides functions for manipulating address + * spaces i.e adding/removing segments and regions etc. + * + * an address space describes a process' useable memory. each address space + * is composed of two sets. + * + * the first set describes the segments held by this address space, in other + * words the physical memory. note that since every segment is unique to + * the whole system, the first set actually contains the identifiers of + * the segments belonging to this address space. therefore, whenever a + * segment is reserved for this address space, the segment manager + * automatically adds the segment identifiers to the address space's set + * of segments. + * + * the second set describes the regions, the virtual areas which reference + * some, or all, of the address space's segments. unlike segments, regions + * live in a specific address space and have no meaning for other address + * spaces or the system itself. therefore, the address space's set of + * regions contains the actual region objects. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * include the machine-specific definitions required by the core. + */ + +machine_include(as); + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the kernel manager. + */ + +extern m_kernel _kernel; + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the address space manager. + */ + +m_as _as; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function shows a precise address space by displaying its segments + * and regions. + * + * steps: + * + * 1) retrieve the address space object. + * 2) display general information such as the task the address space + * lives in, the address space id etc. + * 3) display the segments held by the address space. + * 4) display the regions held by the address space. + * 5) call machine. + */ + +t_error as_show(i_as id, + mt_margin margin) +{ + i_segment* segment; + o_region* region; + t_setsz size; + t_state st; + s_iterator i; + o_as* o; + + /* + * 1) + */ + + if (as_get(id, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the address space object"); + + /* + * 2) + */ + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + "address space:\n", + MODULE_CONSOLE_MARGIN_VALUE(margin)); + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " core: id(%qd) task(%qd) segments(%qd) regions(%qd)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + o->id, + o->task, + o->segments, + o->regions); + + /* + * 3) + */ + + if (set_size(o->segments, &size) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the size of the set of segments"); + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " segments: id(%qd) size(%qd)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + o->segments, + size); + + set_foreach(SET_OPTION_FORWARD, o->segments, &i, st) + { + if (set_object(o->segments, i, (void**)&segment) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the segment identifier"); + + if (segment_show(*segment, + margin + 3 * MODULE_CONSOLE_MARGIN_SHIFT) != ERROR_OK) + CORE_ESCAPE("unable to show the segment"); + } + + /* + * 4) + */ + + if (set_size(o->regions, &size) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the size of the set of regions"); + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " regions: id(%qd) size(%qd)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + o->regions, + size); + + set_foreach(SET_OPTION_FORWARD, o->regions, &i, st) + { + if (set_object(o->regions, i, (void**)®ion) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + if (region_show(o->id, + region->id, + margin + 3 * MODULE_CONSOLE_MARGIN_SHIFT) != ERROR_OK) + CORE_ESCAPE("unable to show the region"); + } + + /* + * 5) + */ + + if (machine_call(as, show, id, margin) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function dumps the address spaces manager. + * + * steps: + * + * 1) display general information. + * 2) show the identifier object. + * 3) retrieve the set's size. + * 4) for each address space held by the manager, show it. + * 5) call the machine. + */ + +t_error as_dump(void) +{ + t_setsz size; + o_as* o; + t_state st; + s_iterator i; + + /* + * 1) + */ + + module_call(console, message, + '#', "address space manager: ass(%qd)\n", + _as.ass); + + module_call(console, message, + '#', " core: ass(%qd)\n", + _as.ass); + + /* + * 2) + */ + + if (id_show(&_as.id, + 2 * MODULE_CONSOLE_MARGIN_SHIFT) != ERROR_OK) + CORE_ESCAPE("unable to show the identifier object"); + + /* + * 3) + */ + + if (set_size(_as.ass, &size) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the size of the set"); + + /* + * 4) + */ + + module_call(console, message, + '#', " address spaces: id(%qd) size(%qd)\n", + _as.ass, + size); + + set_foreach(SET_OPTION_FORWARD, _as.ass, &i, st) + { + if (set_object(_as.ass, i, (void**)&o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the address space object"); + + if (as_show(o->id, + 3 * MODULE_CONSOLE_MARGIN_SHIFT) != ERROR_OK) + CORE_ESCAPE("unable to show the address space object"); + } + + /* + * 5) + */ + + if (machine_call(as, dump) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function translates a physical address into its virtual counterpart + * according to the address space's mappings. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the address space object. + * 2) go through the regions in order to locate the mapped segment in which + * the given physical address lies. + * 3) compute the physical address according to the region's offset and + * segment's address. + * 4) call the machine. + */ + +t_error as_virtual(i_as id, + t_paddr physical, + t_vaddr* virtual) +{ + o_as* o; + o_region* region; + o_segment* segment; + t_boolean found; + t_state st; + s_iterator i; + + /* + * 0) + */ + + if (virtual == NULL) + CORE_ESCAPE("the 'virtual' argument is null"); + + /* + * 1) + */ + + if (as_get(id, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the address space object"); + + /* + * 2) + */ + + found = BOOLEAN_FALSE; + + set_foreach(SET_OPTION_FORWARD, o->regions, &i, st) + { + if (set_object(o->regions, i, (void**)®ion) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + if (segment_get(region->segment, &segment) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the segment object"); + + if ((segment->address + region->offset) <= physical && + physical < (segment->address + region->offset + region->size)) + { + found = BOOLEAN_TRUE; + + break; + } + } + + if (found == BOOLEAN_FALSE) + CORE_ESCAPE("unable to locate the segment corresponding to the given " + "physical address"); + + /* + * 3) + */ + + *virtual = region->address + + (physical - (segment->address + region->offset)); + + /* + * 4) + */ + + if (machine_call(as, virtual, id, physical, virtual) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function translates a virtual address into its physical counterpart + * according to the given address space mappings. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the address space object. + * 2) locate the region in which the given virtual address lies. + * 3) retrieve the segment that the located region maps. + * 4) compute the physical address. + * 5) call the machine. + */ + +t_error as_physical(i_as id, + t_vaddr virtual, + t_paddr* physical) +{ + o_region* region; + o_segment* segment; + t_uint32 found; + o_as* o; + s_iterator i; + t_state st; + + /* + * 0) + */ + + if (physical == NULL) + CORE_ESCAPE("the 'physical' argument is null"); + + /* + * 1) + */ + + if (as_get(id, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the address space object"); + + /* + * 2) + */ + + found = BOOLEAN_FALSE; + + set_foreach(SET_OPTION_FORWARD, o->regions, &i, st) + { + if (set_object(o->regions, i, (void**)®ion) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + if (region->address <= virtual && + virtual < (region->address + region->size)) + { + found = BOOLEAN_TRUE; + + break; + } + + if (region->address > virtual) + CORE_ESCAPE("unable to locate the region containing the given " + "virtual address"); + } + + if (found == BOOLEAN_FALSE) + CORE_ESCAPE("unable to locate the region corresponding to the given " + "virtual address"); + + /* + * 3) + */ + + if (segment_get(region->segment, &segment) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the segment object"); + + /* + * 4) + */ + + *physical = segment->address + region->offset + (virtual - region->address); + + /* + * 5) + */ + + if (machine_call(as, physical, id, virtual, physical) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function reads data from a virtual location in the address space. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the address space object. + * 2) retrieve the first region of the address space. + * 3) locate the first region to read from according to the source virtual + * address. + * 4) compute the offset and size to read from this first region. + * 5) read data directly from the region's segment. + * 6) loop throught the remaining regions, if required. + * a) retrieve the following region. + * b) compute the offset and size for this new region. + * c) read data directly from the region's segment. + * 7) call the machine. + */ + +t_error as_read(i_as id, + t_vaddr source, + t_vsize size, + void* destination) +{ + t_uint8* buffer = destination; + o_region* region; + o_region* previous; + t_paddr offset; + s_iterator next; + t_psize copy; + o_as* o; + s_iterator i; + + /* + * 0) + */ + + if (size == 0) + CORE_LEAVE(); + + if (destination == NULL) + CORE_ESCAPE("the 'destination' argument is null"); + + /* + * 1) + */ + + if (as_get(id, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the address space object"); + + /* + * 2) + */ + + if (set_head(o->regions, &i) != ERROR_TRUE) + CORE_ESCAPE("unable to locate the set's head"); + + if (set_object(o->regions, i, (void**)®ion) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + /* + * 3) + */ + + while (region->address + region->size <= source) + { + if (set_next(o->regions, i, &next) != ERROR_TRUE) + CORE_ESCAPE("unable to locate the next set's object"); + + if (set_object(o->regions, next, (void**)®ion) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + memcpy(&i, &next, sizeof (s_iterator)); + } + + if (region->address > source) + CORE_ESCAPE("the located region's address is higher than the " + "source address"); + + /* + * 4) + */ + + offset = source - region->address; + + copy = region->size - offset; + + if (copy > size) + copy = size; + + offset += region->offset; + + /* + * 5) + */ + + if (segment_read(region->segment, offset, buffer, copy) != ERROR_OK) + CORE_ESCAPE("unable to read from the segment"); + + buffer += copy; + size -= copy; + + if (size == 0) + CORE_LEAVE(); + + /* + * 6) + */ + + for (;;) + { + previous = region; + + /* + * a) + */ + + if (set_next(o->regions, next, &i) != ERROR_TRUE) + CORE_ESCAPE("unable to retrieve the next set's object"); + + if (set_object(o->regions, i, (void**)®ion) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + if (region->address != previous->address + previous->size) + CORE_ESCAPE("unable to read from non-adjacent regions"); + + /* + * b) + */ + + offset = region->offset; + + copy = region->size; + + if (copy > size) + copy = size; + + /* + * c) + */ + + if (segment_read(region->segment, offset, buffer, copy) != ERROR_OK) + CORE_ESCAPE("unable to read from the segment"); + + buffer += copy; + size -= copy; + + if (size == 0) + break; + + memcpy(&next, &i, sizeof (s_iterator)); + } + + /* + * 7) + */ + + if (machine_call(as, read, id, source, size, destination) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function writes data to an address space. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the address space object. + * 2) retrieve the first region of the adress space. + * 3) locate the the first region to write data to. + * 4) compute the offset and size for this first region. + * 5) write data to the underlying segment. + * 6) loop throught the remaining regions, if required. + * a) retrieve the following region. + * b) compute the offset and size for this new region. + * c) write data to the region's segment. + * 7) call the machine. + */ + +t_error as_write(i_as id, + const void* source, + t_vsize size, + t_vaddr destination) +{ + const t_uint8* buffer = source; + o_region* region; + o_region* previous; + t_paddr offset; + s_iterator next; + t_psize copy; + o_as* o; + s_iterator i; + + /* + * 0) + */ + + if (size == 0) + CORE_LEAVE(); + + if (source == NULL) + CORE_ESCAPE("the 'source' argument is null"); + + /* + * 1) + */ + + if (as_get(id, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the address space object"); + + /* + * 2) + */ + + if (set_head(o->regions, &i) != ERROR_TRUE) + CORE_ESCAPE("unable to locate the set's head"); + + if (set_object(o->regions, i, (void**)®ion) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + /* + * 3) + */ + + while (region->address + region->size <= destination) + { + if (set_next(o->regions, i, &next) != ERROR_TRUE) + CORE_ESCAPE("unable to locate the next set's object"); + + if (set_object(o->regions, next, (void**)®ion) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + memcpy(&i, &next, sizeof (s_iterator)); + } + + if (region->address > destination) + CORE_ESCAPE("the located region's address is higher than the " + "destination address"); + + /* + * 4) + */ + + offset = destination - region->address; + + copy = region->size - offset; + + if (copy > size) + copy = size; + + offset += region->offset; + + /* + * 5) + */ + + if (segment_write(region->segment, offset, buffer, copy) != ERROR_OK) + CORE_ESCAPE("unable to write to the segment"); + + buffer += copy; + size -= copy; + + if (size == 0) + CORE_LEAVE(); + + /* + * 6) + */ + + for (;;) + { + previous = region; + + /* + * a) + */ + + if (set_next(o->regions, next, &i) != ERROR_TRUE) + CORE_ESCAPE("unable to locate the next set's object"); + + if (set_object(o->regions, i, (void**)®ion) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + if (region->address != previous->address + previous->size) + CORE_ESCAPE("unable to write to non-adjacent regions"); + + /* + * b) + */ + + offset = region->offset; + + copy = region->size; + + if (copy > size) + copy = size; + + /* + * c) + */ + + if (segment_write(region->segment, offset, buffer, copy) != ERROR_OK) + CORE_ESCAPE("unable to write to the segment"); + + buffer += copy; + size -= copy; + + if (size == 0) + break; + + memcpy(&next, &i, sizeof (s_iterator)); + } + + /* + * 7) + */ + + if (machine_call(as, write, id, source, size, destination) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function copies data from an address space to another. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the address space object. + * 2) locate the first region to copy data from. + * 3) locate the first region to copy data to. + * 4) compute the start offsets and sizes for both the source and + * destination regions. + * a) perform the copy. + * b) proceed to the following source region, if necessary. + * c) proceed to the followgin destination region, if necessary. + * 5) call the machine. + */ + +t_error as_copy(i_as source_id, + t_vaddr source_address, + i_as destination_id, + t_vaddr destination_address, + t_vsize size) +{ + o_as* source_o; + o_region* source_region; + o_segment* source_segment; + o_as* destination_o; + o_region* destination_region; + o_segment* destination_segment; + s_iterator source_i; + s_iterator source_next; + s_iterator destination_i; + s_iterator destination_next; + t_paddr source_offset; + t_paddr destination_offset; + t_psize copy; + t_psize source_copy; + t_psize destination_copy; + o_region* previous; + s_iterator next; + + /* + * 0) + */ + + if (size == 0) + CORE_LEAVE(); + + /* + * 1) + */ + + if (as_get(source_id, &source_o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the address space object"); + + if (as_get(destination_id, &destination_o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the address space object"); + + /* + * 2) + */ + + if (set_head(source_o->regions, &source_i) != ERROR_TRUE) + CORE_ESCAPE("unable to locate the set's head"); + + if (set_object(source_o->regions, + source_i, + (void**)&source_region) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + while (source_region->address + source_region->size <= source_address) + { + if (set_next(source_o->regions, + source_i, + &source_next) != ERROR_TRUE) + CORE_ESCAPE("unable to locate the next set's object"); + + if (set_object(source_o->regions, + source_next, + (void**)&source_region) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + memcpy(&source_i, &source_next, sizeof (s_iterator)); + } + + if (source_region->address > source_address) + CORE_ESCAPE("the located region's address is higher the source " + "virtual address"); + + if (segment_get(source_region->segment, &source_segment) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the segment object"); + + /* + * 3) + */ + + if (set_head(destination_o->regions, &destination_i) != ERROR_TRUE) + CORE_ESCAPE("unable to locate the set's head"); + + if (set_object(destination_o->regions, + destination_i, + (void**)&destination_region) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + while (destination_region->address + destination_region->size <= + destination_address) + { + if (set_next(destination_o->regions, + destination_i, + &destination_next) != ERROR_TRUE) + CORE_ESCAPE("unable to locate the next set's object"); + + if (set_object(destination_o->regions, + destination_next, + (void**)&destination_region) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + memcpy(&destination_i, &destination_next, sizeof (s_iterator)); + } + + if (destination_region->address > destination_address) + CORE_ESCAPE("the located region's address is higher than the " + "destination virtual address"); + + if (segment_get(destination_region->segment, + &destination_segment) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the segment object"); + + /* + * 4) + */ + + source_copy = source_region->size - + (source_address - source_region->address); + destination_copy = destination_region->size - + (destination_address - destination_region->address); + source_offset = source_region->offset + + (source_address - source_region->address); + destination_offset = destination_region->offset + + (destination_address - destination_region->address); + + for (;;) + { + if (source_copy < destination_copy) + copy = source_copy; + else + copy = destination_copy; + + if (copy > size) + copy = size; + + /* + * a) + */ + + if (segment_copy(destination_region->segment, + destination_offset, + source_region->segment, + source_offset, + copy) != ERROR_OK) + CORE_ESCAPE("unable to copy from one segment to another"); + + source_offset += copy; + destination_offset += copy; + + size -= copy; + + source_copy -= copy; + destination_copy -= copy; + + if (!size) + break; + + /* + * b) + */ + + if (source_copy == 0) + { + previous = source_region; + + if (set_next(source_o->regions, source_i, &next) != ERROR_TRUE) + CORE_ESCAPE("unable to locate the next set's object"); + + if (set_object(source_o->regions, + next, + (void**)&source_region) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + if (source_region->address != previous->address + previous->size) + CORE_ESCAPE("unable to copy from non-adjacent regions"); + + memcpy(&source_i, &next, sizeof (s_iterator)); + + if (segment_get(source_region->segment, + &source_segment) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the segment object"); + + source_offset = source_region->offset; + + source_copy = source_region->size; + } + + /* + * c) + */ + + + if (destination_copy == 0) + { + previous = destination_region; + + if (set_next(destination_o->regions, + destination_i, + &next) != ERROR_TRUE) + CORE_ESCAPE("unable to locate the next set's object"); + + if (set_object(destination_o->regions, + next, + (void**)&destination_region) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + if (destination_region->address != + previous->address + previous->size) + CORE_ESCAPE("unable to copy to non-adjacent regions"); + + memcpy(&destination_i, &next, sizeof (s_iterator)); + + if (segment_get(destination_region->segment, + &destination_segment) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the segment object"); + + destination_offset = destination_region->offset; + destination_copy = destination_region->size; + } + } + + /* + * 5) + */ + + if (machine_call(as, copy, + source_id, source_address, + destination_id, destination_address, + size) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function reserves an address space for the given task and returns + * the freshly created address space's identifier. + * + * steps: + * + * 0) verify the arguments. + * 1) reserve an identifier for the address space object. + * 2) retrieve the task object. + * 3) return an error if the task already possesses an address space. + * 4) assign the address space to the task. + * 5) initialize the address space object. + * 6) reserve a set of segments. + * 7) reserve a set of regions. + * 8) add the new address space object into the set of address spaces + * 9) call the machine. + */ + +t_error as_reserve(i_task task, + i_as* id) +{ + o_task* target; + o_as o; + + /* + * 0) + */ + + if (id == NULL) + CORE_ESCAPE("the 'id' argument is null"); + + /* + * 1) + */ + + if (id_reserve(&_as.id, id) != ERROR_OK) + CORE_ESCAPE("unable to reserve an identifier"); + + /* + * 2) + */ + + if (task_get(task, &target) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the task object"); + + /* + * 3) + */ + + if (target->as != ID_UNUSED) + CORE_ESCAPE("the task already possesses an address space"); + + /* + * 4) + */ + + target->as = *id; + + /* + * 5) + */ + + memset(&o, 0x0, sizeof (o_as)); + + o.id = *id; + o.task = target->id; + + /* + * 6) + */ + + if (set_reserve(array, + SET_OPTION_SORT | SET_OPTION_ALLOCATE, + AS_SEGMENTS_INITSZ, + sizeof (i_segment), + &o.segments) != ERROR_OK) + CORE_ESCAPE("unable to reserve the set of segments"); + + /* + * 7) + */ + + if (set_reserve(array, + SET_OPTION_SORT | SET_OPTION_FREE, + AS_REGIONS_INITSZ, + sizeof (o_region), + &o.regions) != ERROR_OK) + CORE_ESCAPE("unable to reserve the set of regions"); + + /* + * 8) + */ + + if (set_add(_as.ass, &o) != ERROR_OK) + CORE_ESCAPE("unable to add the object to the set of address spaces"); + + /* + * 9) + */ + + if (machine_call(as, reserve, task, id) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function releases an address space. + * + * steps: + * + * 1) call the machine. + * 2) retrieve the address space object. + * 3) retrieve the address space's task object and reset the task's + * address space. + * 4) release the address space object identifier. + * 5) flush and release the set of regions. + * 6) flush and release the set of segments. + * 7) removes the address space object from the address space set. + */ + +t_error as_release(i_as id) +{ + o_task* task; + o_as* o; + + /* + * 1) + */ + + if (machine_call(as, release, id) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 2) + */ + + if (as_get(id, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the address space object"); + + /* + * 3) + */ + + if (task_get(o->task, &task) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the task object"); + + task->as = ID_UNUSED; + + /* + * 4) + */ + + if (id_release(&_as.id, o->id) != ERROR_OK) + CORE_ESCAPE("unable to release the identifier"); + + /* + * 5) + */ + + if (region_flush(o->id) != ERROR_OK) + CORE_ESCAPE("unable to flush the regions"); + + if (set_release(o->regions) != ERROR_OK) + CORE_ESCAPE("unable to release the set of regions"); + + /* + * 6) + */ + + if (segment_flush(o->id) != ERROR_OK) + CORE_ESCAPE("unable to flush the segments"); + + if (set_release(o->segments) != ERROR_OK) + CORE_ESCAPE("unable to release the set of segments"); + + /* + * 7) + */ + + if (set_remove(_as.ass, o->id) != ERROR_OK) + CORE_ESCAPE("unable to remove the object from the set of address spaces"); + + CORE_LEAVE(); +} + +/* + * this function returns true if the given address space object exists. + */ + +t_error as_exist(i_as id) +{ + if (set_exist(_as.ass, id) != ERROR_TRUE) + CORE_FALSE(); + + CORE_TRUE(); +} + +/* + * this function retrieves an address space object from the set of + * address spaces. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the object from the set of address spaces. + */ + +t_error as_get(i_as id, + o_as** object) +{ + /* + * 0) + */ + + if (object == NULL) + CORE_ESCAPE("the 'object' argument is null"); + + /* + * 1) + */ + + if (set_get(_as.ass, id, (void**)object) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the object from the set of " + "address spaces"); + + CORE_LEAVE(); +} + +/* + * this function initializes the address space manager. + * + * steps: + * + * 1) display a message. + * 2) initialize the address space manager's structure. + * 3) initialize the identifier object in order to be able to generate + * the address spaces' identifier. + * 4) reserve the set which will contain the future address spaces. + * 5) calls the machine. + */ + +t_error as_initialize(void) +{ + /* + * 1) + */ + + module_call(console, message, + '+', "initializing the address space manager\n"); + + /* + * 2) + */ + + memset(&_as, 0x0, sizeof (m_as)); + + /* + * 3) + */ + + if (id_build(&_as.id) != ERROR_OK) + CORE_ESCAPE("unable to initialize the identifier object"); + + /* + * 4) + */ + + if (set_reserve(ll, + SET_OPTION_ALLOCATE | SET_OPTION_SORT, + sizeof (o_as), + &_as.ass) != ERROR_OK) + CORE_ESCAPE("unable to reserve the set of address spaces"); + + /* + * 5) + */ + + if (machine_call(as, initialize) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function cleans the address space manager. + * + * steps: + * + * 1) display a message. + * 2) call the machine. + * 3) flush the address spaces. note that the kernel address space is + * never released in order to keep the kernel running. + * 4) release the set of address spaces. + * 5) destroy the identifier object. + */ + +t_error as_clean(void) +{ + o_as* o; + s_iterator i; + + /* + * 1) + */ + + module_call(console, message, + '+', "cleaning the address space manager\n"); + + /* + * 2) + */ + + if (machine_call(as, clean) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 3) + */ + + while (set_head(_as.ass, &i) == ERROR_TRUE) + { + if (set_object(_as.ass, i, (void**)&o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the object from the set of " + "address spaces"); + + if (o->id == _kernel.as) + { + if (set_remove(_as.ass, o->id) != ERROR_OK) + CORE_ESCAPE("unable to remove the kernel address space identifier " + "from the set of address spaces"); + } + else + { + if (as_release(o->id) != ERROR_OK) + CORE_ESCAPE("unable to release the address space object"); + } + } + + /* + * 4) + */ + + if (set_release(_as.ass) != ERROR_OK) + CORE_ESCAPE("unable to release the set of address spaces"); + + /* + * 5) + */ + + if (id_destroy(&_as.id) != ERROR_OK) + CORE_ESCAPE("unable to destroy the identifier object"); + + CORE_LEAVE(); +} diff --git a/kaneton/core/capability/Makefile b/kaneton/core/capability/Makefile new file mode 100644 index 0000000..c4da73e --- /dev/null +++ b/kaneton/core/capability/Makefile @@ -0,0 +1,76 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/buckman/kaneton/kaneton/core/capability/Makefile +# +# created julien quintard [thu jun 28 20:30:19 2007] +# updated matthieu bucchianeri [sat sep 1 12:31:47 2007] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/core/capability + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +CAPABILITY_C := capability.c + +CAPABILITY_O := $(CAPABILITY_C:.c=.o) + +CAPABILITY_INCLUDE := $(_CORE_INCLUDE_DIR_)/capability.h + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(_CAPABILITY_LO_) + +$(_CAPABILITY_LO_): $(CAPABILITY_O) + $(call env_remove,$(_CAPABILITY_LO_),) + + $(call env_archive,$(_CAPABILITY_LO_),$(CAPABILITY_O),) + +clear: + $(call env_remove,$(CAPABILITY_O),) + + $(call env_remove,$(_CAPABILITY_LO_),) + + $(call env_purge,) + +prototypes: + $(call env_prototypes,$(CAPABILITY_INCLUDE),) + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(CAPABILITY_C),) + +dependencies: + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/core/capability/capability.c b/kaneton/core/capability/capability.c new file mode 100644 index 0000000..cedd7ba --- /dev/null +++ b/kaneton/core/capability/capability.c @@ -0,0 +1,611 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/capability/capability.c + * + * created julien quintard [sun jun 3 19:48:52 2007] + * updated julien quintard [sun jan 30 20:52:51 2011] + */ + +// XXX THIS MANAGER MUST BE RE-DEVELOPED IN ITS ENTIRETY!!! + +/* + * ---------- information ----------------------------------------------------- + * + * the capability manager provides an interface to manipulate the + * capabilities. + * + * although the capabilities over the distributed system will have different + * formats, the kaneton core uses a unique capability format for every + * kaneton object. + * + * note that every capability format over the distributed system must + * begins with a 128-bit field describing the node identifier. + * + * the kaneton core capability format is composed of a node identifier on + * 64 bits specifying the task of the distributed system which had generated + * this capability, an object identifier on 64 bits, an operation field on + * 32 bits describing the allowed operations and finally a check field on + * 64 bits to achieve security of the capability. + * + * the node identifier is used to locate a service independently of the + * machine on which it resides. indeed, if a task is migrated on another + * machine, the node identifier of this task will remain the same. then + * the user programs of the service will continue to work properly. + * + * to verify the correctness of the capabilities used by the user programs, + * the core needs to store the check fields of each capability previously + * generated. these check fields will be stored in set objects although + * a check field is not a kaneton object. indeed, an identifier is + * included in the capability descriptor structure and will take the value + * of the check field since it is a 64-bit number too. in addition, other + * informations are stored with each check field, including the capability + * descriptor's identifier of the capability from which this capability was + * built and a set of capabilities generated from this capability. + * + * recall that the capabilities permit a program to restrict the permissions + * of a capability and then to distribute this new generated capability to + * other tasks. but the kaneton capability system also provides other + * interesting features including the possibility to restrict a previously + * restricted capability and so on but also to invalidate a restricted + * capability. this last feature is very interesting because it permit + * a program to distribute privileges and then to simply forbid the use + * of capabilities it previously forged. + * + * finally, to avoid check fields collisions, a salt is used. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the capability manager variable. + */ + +m_capability _capability; + +/* + * the kernel manager variable. + */ + +extern m_kernel _kernel; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * Make a checksum on a capability, this one is simple but can be easily change + */ +// XXX +static t_error simple_checksum(char* data, + t_uint32 size, + t_uint64* res) +{ + *res = (t_uint64)sum2((char*)&data, size); + + CORE_LEAVE(); +} + +/* + * Display a capability, used for debugging purpose + */ + +t_error capability_show(t_id id) +{ + t_capability_descriptor* descriptor; + + if (set_get(_capability.descriptors, id, (void**)&descriptor) != ERROR_OK) + CORE_ESCAPE("XXX"); + + module_call(console, message, + '#', " [%8qd] node: %qd:%qd object: %qd operations: %024b\n", + descriptor->id, + descriptor->capability.node.cell, + descriptor->capability.node.task, + descriptor->capability.object, + descriptor->capability.operations); + + module_call(console, message, + '#', " check: %qd parent: %qd children:\n", + descriptor->check, + descriptor->parent); + + if (descriptor->children != ID_UNUSED) + { + t_state state; + t_id* data; + s_iterator i; + + set_foreach(SET_OPTION_FORWARD, descriptor->children, &i, state) + { + if (set_object(descriptor->children, i, (void**)&data) != ERROR_OK) + CORE_ESCAPE("XXX"); + + module_call(console, message, + '#', " [child] %qd\n", *data); + } + } + + CORE_LEAVE(); +} + +/* + * Dumps the whole capabilities in the capability set + * + * 1) Gets the size of the capability set + * 2) Displays the numbers of capabilities + * 3) Displays all the capabilities +displays + */ + +t_error capability_dump(void) +{ + t_state state; + t_capability_descriptor* data; + t_setsz size; + s_iterator i; + + /* + * 1) + */ + + if (set_size(_capability.descriptors, &size) != ERROR_OK) + CORE_ESCAPE("XXX"); + + /* + * 2) + */ + + module_call(console, message, + '#', "dumping %qd capability descriptor(s) from the " + "capability set:\n", size); + + /* + * 3) + */ + + set_foreach(SET_OPTION_FORWARD, _capability.descriptors, &i, state) + { + if (set_object(_capability.descriptors, i, (void**)&data) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (capability_show(data->id) != ERROR_OK) + CORE_ESCAPE("XXX"); + } + + CORE_LEAVE(); +} + + +/* + * Reserves a capability + * + * 1) Setting the values of the capability + * 2) Getting an id for the capability descriptor + * 3) Setting the values of the capability descriptor and calculating the checksum + * 4) Adding the capability to the set + */ + + /* + * XXX the children set object is not reserved yet since it is likely that no + * capabilities will be restricted. so, to avoid wasting space, no set + * is reserved. + */ + +t_error capability_reserve(t_id object, + t_operations operations, + t_capability* new) +{ + t_capability_descriptor descriptor; + + /* + * 1) + */ + + new->node.cell = _kernel.cell; + new->node.task = _kernel.task; + new->object = object; + new->operations = operations; + + /* + * 2) + */ + // XXX should be a 64-bit random number generation + new->descriptor = (t_uint64)sum2((char*)&new, sizeof (t_capability)); + + /* + * 3) + */ + + descriptor.id = new->descriptor; + if (_capability.f_checksum((char *)new, sizeof (t_capability), &descriptor.check) != + ERROR_OK) + CORE_ESCAPE("XXX"); + descriptor.capability = *new; + descriptor.parent = ID_UNUSED; + descriptor.children = ID_UNUSED; + + /* + * 4) + */ + if (set_add(_capability.descriptors, &descriptor) != ERROR_OK) + CORE_ESCAPE("XXX"); + + CORE_LEAVE(); +} + +/* + * Releases a capability descriptor, if the capability has children it won't be deleted + * + * 1) Gets ths descriptor and then check if it has a parent + * 2) Invalidates all the children of the released capability + * 3) Removes the capability from the capability descriptor set + */ + +t_error capability_release(t_id id) +{ + t_capability_descriptor* descriptor; + t_state state; + t_id* data; + s_iterator i; + + /* + * 1) + */ + + if (capability_get(id, &descriptor) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (descriptor->parent != ID_UNUSED) + CORE_ESCAPE("XXX"); + + /* + * 2) + */ + if (descriptor->children != ID_UNUSED) + { + set_foreach(SET_OPTION_FORWARD, descriptor->children, &i, state) + { + if (set_object(descriptor->children, i, (void**)&data) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (capability_invalidate(descriptor->id, *data) != ERROR_OK) + CORE_ESCAPE("XXX"); + } + + if (set_release(descriptor->children) != ERROR_OK) + CORE_ESCAPE("XXX"); + } + + /* + * 3) + */ + if (set_remove(_capability.descriptors, descriptor->id) != ERROR_OK) + CORE_ESCAPE("XXX"); + + CORE_LEAVE(); +} + +/* + * Creates a new restricted capability from another capability + * + * 1) Gets the capability + * 2) Checking that operations are really restriction of the parent capability + * 3) Setting the values of the new capability and its associated descriptor + * 4) Reserve a set on the parent capability if it doesn't have any child and + * add the new capability to the set of children of the parent + */ + +t_error capability_restrict(t_id id, + t_operations operations, + t_capability* new) +{ + t_capability_descriptor restricted; + t_capability_descriptor* parent; + + /* + * 1) + */ + if (capability_get(id, &parent) != ERROR_OK) + CORE_ESCAPE("XXX"); + + /* + * 2) + */ + if ((parent->capability.operations | operations) != + parent->capability.operations) + CORE_ESCAPE("XXX"); + + /* + * 3) + */ + new->node = parent->capability.node; + new->object = parent->capability.object; + new->operations = operations; + + // XXX should be a 64-bit random number generation + new->descriptor = (t_uint64)sum2((char*)&new, sizeof (t_capability)); + + restricted.id = new->descriptor; + if (_capability.f_checksum((char *)new, sizeof (t_capability), &restricted.check) != + ERROR_OK) + CORE_ESCAPE("XXX"); + restricted.capability = *new; + restricted.parent = parent->id; + restricted.children = ID_UNUSED; + + if (set_add(_capability.descriptors, &restricted) != ERROR_OK) + CORE_ESCAPE("XXX"); + + + /* + * 4) + */ + if (parent->children == ID_UNUSED) + if (set_reserve(array, SET_OPTION_ALLOCATE, CAPABILITY_CHILDREN_INITSZ, + sizeof (t_id), &parent->children) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (set_add(parent->children, &restricted.id) != ERROR_OK) + { + CORE_ESCAPE("XXX"); + } + + CORE_LEAVE(); +} + +/* + * Invalidates a previously restricted capability + */ + +t_error capability_invalidate(t_id p, + t_id c) +{ + t_capability_descriptor* restricted; + t_capability_descriptor* parent; + t_state state; + t_id* data; + s_iterator i; + + if (capability_get(p, &parent) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (capability_get(c, &restricted) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (restricted->parent != parent->id) + CORE_ESCAPE("XXX"); + + if (set_remove(parent->children, restricted->id) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (restricted->children != ID_UNUSED) + { + set_foreach(SET_OPTION_FORWARD, restricted->children, &i, state) + { + if (set_object(restricted->children, i, (void**)&data) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (capability_invalidate(c, *data) != ERROR_OK) + CORE_ESCAPE("XXX"); + } + + if (set_release(restricted->children) != ERROR_OK) + CORE_ESCAPE("XXX"); + } + + if (set_remove(_capability.descriptors, restricted->id) != ERROR_OK) + CORE_ESCAPE("XXX"); + + CORE_LEAVE(); +} + +/* + * return true if the given capability exists. + */ + +t_error capability_exist(t_id id) +{ + if (set_exist(_capability.descriptors, id) != ERROR_TRUE) + CORE_FALSE(); + + CORE_TRUE(); +} + +/* + * Gets a capability descriptor corresponding to an id + */ + +t_error capability_get(t_id id, + t_capability_descriptor** descriptor) +{ + if (set_get(_capability.descriptors, id, (void**)descriptor) != ERROR_OK) + CORE_ESCAPE("XXX"); + + CORE_LEAVE(); +} + + +/* + * Gives a capability to another node + * 1) Gets the descriptor + * 2) Sets the new node in the capability + * 3) Updates the checksum + */ + +t_error capability_give(t_id id, + i_node node) +{ + t_capability_descriptor* descriptor; + + /* + * 1) + */ + if (capability_get(id, &descriptor) != ERROR_OK) + CORE_ESCAPE("XXX"); + + /* + * 2) + */ + descriptor->capability.node = node; + + /* + * 3) + */ + if (_capability.f_checksum((char *)&descriptor->capability, + sizeof (t_capability), + &descriptor->check) != ERROR_OK) + CORE_ESCAPE("XXX"); + + CORE_LEAVE(); +} + + +/* + * Vefifies if a capability is correct by checking the value of the checksum + * 1) Gets the descriptor + * 2) Generates a correct checksum for the provided capability + * 3) Compares the generated checksum with the one saved in the descriptor + */ + +t_error capability_verify(t_capability* provided) +{ + t_capability_descriptor* current; + t_uint64 check; + + /* + * 1) + */ + if (capability_get((t_id)provided->descriptor, ¤t) != ERROR_OK) + CORE_ESCAPE("XXX"); + + /* + * 2) + */ + if (_capability.f_checksum((char *)provided, sizeof (t_capability), &check) != + ERROR_OK) + CORE_ESCAPE("XXX"); + + /* + * 3) + */ + if (check != current->check) + CORE_ESCAPE("XXX"); + + CORE_LEAVE(); +} + + +/* + * Init + */ + +t_error capability_initialize(void) +{ + /* + * 1) + */ + + module_call(console, message, + '+', "initializing the capability manager\n"); + + /* + * XXX + */ + + memset(&_capability, 0x0, sizeof (m_capability)); + + /* + * 2) + */ + + if (set_reserve(ll, SET_OPTION_ALLOCATE | SET_OPTION_SORT, + sizeof (t_capability_descriptor), + &_capability.descriptors) != ERROR_OK) + CORE_ESCAPE("XXX"); + + /* + * 3) + */ + _capability.f_checksum = simple_checksum; + +#if (DEBUG & DEBUG_CAPABILITY) + capability_dump(); +#endif + +#if 0 // XXX + t_capability c; + t_capability r1; + t_capability r2; + t_capability f; + + capability_reserve(42, 0x17, &c); + + capability_restrict((t_id)c.descriptor, 0x11, &r1); + capability_restrict((t_id)c.descriptor, 0x7, &r2); + + capability_restrict((t_id)r1.descriptor, 0x1, &f); + + capability_dump(); + + capability_verify(&c); + capability_verify(&f); + capability_verify(&r1); + capability_verify(&r2); +#endif + + CORE_LEAVE(); +} + +/* + * Cleaning + */ + +t_error capability_clean(void) +{ + t_capability_descriptor* data; + s_iterator i; + + /* + * XXX + */ + + module_call(console, message, + '+', "cleaning the capability manager\n"); + + /* + * 1) + */ + + while (set_head(_capability.descriptors, &i) == ERROR_TRUE) + { + if (set_object(_capability.descriptors, i, (void**)&data) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (capability_release(data->id) != ERROR_OK) + CORE_ESCAPE("XXX"); + } + + if (set_release(_capability.descriptors) != ERROR_OK) + CORE_ESCAPE("XXX"); + + CORE_LEAVE(); +} + +// XXX si erreur, alors liberer ce qui vient d etre construit + +// XXX changer car actuellement on a pas de fonctions de hash correct + +// XXX fonction random pour les id ou pas? cela est-il vraiment necessaire? diff --git a/kaneton/core/clock/Makefile b/kaneton/core/clock/Makefile new file mode 100644 index 0000000..14d3108 --- /dev/null +++ b/kaneton/core/clock/Makefile @@ -0,0 +1,76 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/kaneton/core/clock/Makefile +# +# created julien quintard [sun jun 10 17:11:24 2007] +# updated julien quintard [fri dec 10 15:28:04 2010] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/core/time + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +CLOCK_C := clock.c + +CLOCK_O := $(CLOCK_C:.c=.o) + +CLOCK_INCLUDE := $(_CORE_INCLUDE_DIR_)/clock.h + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(_CLOCK_LO_) + +$(_CLOCK_LO_): $(CLOCK_O) + $(call env_remove,$(_CLOCK_LO_),) + + $(call env_archive,$(_CLOCK_LO_),$(CLOCK_O),) + +clear: + $(call env_remove,$(CLOCK_O),) + + $(call env_remove,$(_CLOCK_LO_),) + + $(call env_purge,) + +prototypes: + $(call env_prototypes,$(CLOCK_INCLUDE),) + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(CLOCK_C),) + +dependencies: + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/core/clock/clock.c b/kaneton/core/clock/clock.c new file mode 100644 index 0000000..15a41e2 --- /dev/null +++ b/kaneton/core/clock/clock.c @@ -0,0 +1,217 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/clock/clock.c + * + * created julien quintard [wed nov 24 18:40:55 2010] + * updated julien quintard [sun jan 30 20:04:24 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this manager provides functionalities related to the current date and time. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * include the machine-specific definitions required by the core. + */ + +machine_include(clock); + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the clock manager. + */ + +m_clock _clock; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function displays the attributes of the given clock's state. + * + * steps: + * + * 0) verify the arguments. + * 1) display the clock's attributes. + * 2) call the machine. + */ + +t_error clock_show(s_clock* clock, + mt_margin margin) +{ + /* + * 0) + */ + + if (clock == NULL) + CORE_ESCAPE("the 'clock' argument is null"); + + /* + * 1) + */ + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + "clock:\n", + MODULE_CONSOLE_MARGIN_VALUE(margin)); + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " core: year(%u) month(%u) day(%u) hour(%u) minute(%u) " + "second(%u) millisecond(%u)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + clock->year, + clock->month, + clock->day, + clock->hour, + clock->minute, + clock->second, + clock->millisecond); + + /* + * 2) + */ + + if (machine_call(clock, show, clock, margin) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function must be called in order to update the clock by the given + * number of milliseconds. + * + * this is the reponsability of the timer manager to update the clock on + * a regular basis. + * + * steps: + * + * 1) call the machine. + */ + +t_error clock_update(t_uint32 millisecond) +{ + /* + * 1) + */ + + if (machine_call(clock, update, millisecond) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function returns, through the given clock structure, the current + * date and time. + * + * since this functionality depends on the hardware internals, this + * operation is left to the machine. + * + * steps: + * + * 0) verify the arguments. + * 1) call the machine. + */ + +t_error clock_current(s_clock* clock) +{ + /* + * 0) + */ + + if (clock == NULL) + CORE_ESCAPE("the 'clock' argument is null"); + + /* + * 1) + */ + + if (machine_call(clock, current, clock) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function initializes the clock manager. + * + * steps: + * + * 1) display a message. + * 2) initialize memory for the manager's structure. + * 3) call the machine. + */ + +t_error clock_initialize(void) +{ + /* + * 1) + */ + + module_call(console, message, + '+', "initializing the clock manager\n"); + + /* + * 2) + */ + + memset(&_clock, 0x0, sizeof (m_clock)); + + /* + * 3) + */ + + if (machine_call(clock, initialize) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function cleans the clock manager. + * + * steps: + * + * 1) display a message. + * 2) call the machine. + */ + +t_error clock_clean(void) +{ + /* + * 1) + */ + + module_call(console, message, + '+', "cleaning the clock manager\n"); + + /* + * 2) + */ + + if (machine_call(clock, clean) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} diff --git a/kaneton/core/core.c b/kaneton/core/core.c new file mode 100644 index 0000000..7e53ba2 --- /dev/null +++ b/kaneton/core/core.c @@ -0,0 +1,318 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/core.c + * + * created julien quintard [fri feb 11 03:04:40 2005] + * updated julien quintard [sat apr 23 12:34:20 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file is the entry point of the kaneton microkernel. + * + * from the kaneton() function, the kernel manager will be initialized + * and then cleaned. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * this variable is generated when the kernel is compiled, indicating + * the current kernel version. + */ + +extern const char version[]; + +/* + * init variable received from the bootloader specifying segments, regions, + * physical memory layout etc. + */ + +s_init* _init; + +/* + * this variable is filled by the task manager when it injects the + * pre-reserved segments. this variable holds the identifier of the + * segment containing the system service code. + * + * indeed, the kernel needs to retrieve this segment to map it and then + * build a task for this very first service. + */ + +i_segment _system; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function simply initializes and cleans the kernel manager. + * + * steps: + * + * 1) set the _init variable from the boot loader argument. + * 2) load some fundamental modules. + * 3) displays the current kaneton version. + * 4) set up the fine grained allocator. + * 5) start the kernel. + * 6) run the test system, should have it be loaded. + * 7) spawn the 'system' service. + * 8) start the scheduler. + * 9) start the event processing, action which will have the effet of + * activating the hardware interrupts, hence trigger context switchs. then, + * should the execution come back to the kernel, its first task is to + * actually disable the events so that the execution does not go away. + * 10) stop the kernel. + * 11) unload the modules. + * 12) shutdown the system. + */ + +void kaneton(s_init* init) +{ + i_cpu cpu; + + /* + * 1) + */ + + _init = init; + + /* + * 2) + */ + + module_load(console); + module_load(report); + module_load(forward); + module_load(test); + + /* + * 3) + */ + + module_call(console, message, + '+', "%s\n", version); + + /* + * 4) + */ + + module_call(console, message, + '+', "setting up the fine-graind memeory allocator\n"); + + alloc_init(_init->alloc, _init->allocsz); + + /* + * 5) + */ + + module_call(console, message, + '+', "starting the kernel\n"); + + assert(kernel_initialize() == ERROR_OK); + + /* + * 6) + */ + + module_call(test, run); + + /* + * 7) + */ + + module_call(console, message, + '+', "spawning the 'system' server\n"); + + assert(kaneton_spawn() == ERROR_OK); + + /* + * 8) + */ + + assert(cpu_current(&cpu) == ERROR_OK); + + assert(scheduler_start(cpu) == ERROR_OK); + + /* + * 9) + */ + + assert(event_enable() == ERROR_OK); + assert(event_disable() == ERROR_OK); + + /* + * 10) + */ + + module_call(console, message, + '+', "stopping the kernel\n"); + + assert(kernel_clean() == ERROR_OK); + + /* + * 11) + */ + + module_unload(test); + module_unload(forward); + module_unload(report); + module_unload(console); + + /* + * 12) + */ + + while (1) + ; +} + +/* + * this function launches the very first server by creating a new + * task. + * + * the new task's address space is only composed of a stack and the + * code which is a mapping of the code provided by the booloader: + * init->scode. + * + * this first server is referred to as the 'system' server. + * + * steps: + * + * 1) check that the boot loader provided a system to spawn through + * the _slocation_ and _scodesz_ attributes of the init structure. + * 2) reserve a task and addresse space for the server. + * 3) map the server's code segment, i.e _system (c.f. initialize() in task.c) + * at a very precise location. + * 4) map the array of inputs (servers to be launch at boot time by the + * 'system' service) so that the server can access and spawn them. note + * that the inputs are mapped so that the physical and virtual addresses + * match, method referred to as identity mapping. + * 5) reserve a thread. + * 6) builds an arguments array containing the address of the inputs array. + * place these arguments so that the thread can retrieve them in the + * main() function. + * 7) set the task as running. + * 8) start the thread. + */ + +t_error kaneton_spawn(void) +{ + i_thread thread; + i_region region; + i_segment segment; + i_task task; + i_as as; + struct + { + i_task task; + i_as as; + int argc; + char* argv; + char* envp; + } arguments; + + /* + * 1) + */ + + if (_init->scodesz == 0) + CORE_ESCAPE("there is no 'system' server to launch"); + + /* + * 2) + */ + + if (task_reserve(TASK_CLASS_SERVICE, + TASK_BEHAVIOUR_TIMESHARING, + TASK_PRIORITY_TIMESHARING, + &task) != ERROR_OK) + CORE_ESCAPE("unable to reserve a task"); + + if (as_reserve(task, &as) != ERROR_OK) + CORE_ESCAPE("unable to reserve an address space"); + + /* + * 3) + */ + + if (region_reserve(as, + _system, + 0, + REGION_OPTION_FORCE, + _init->slocation, + _init->scodesz, + ®ion) != ERROR_OK) + CORE_ESCAPE("unable to reserve the region for the server code"); + + /* + * 4) + */ + + if (segment_locate((t_paddr)_init->inputs, &segment) == ERROR_FALSE) + CORE_ESCAPE("unable to locate the segment holding the given address"); + + if (region_reserve(as, + segment, + 0, + REGION_OPTION_FORCE, + (t_vaddr)_init->inputs, + _init->inputssz, + ®ion) != ERROR_OK) + CORE_ESCAPE("unable to reserve the region for the inputs"); + + /* + * 5) + */ + + if (thread_reserve(task, + THREAD_PRIORITY, + THREAD_STACK_ADDRESS_NONE, + THREAD_STACK_SIZE, + _init->sentry, + &thread) != ERROR_OK) + CORE_ESCAPE("unable to reserve a thread"); + + /* + * 6) + */ + + arguments.task = task; + arguments.as = as; + arguments.argc = 1; + arguments.argv = (char*)_init->inputs; + arguments.envp = NULL; + + if (thread_arguments(thread, &arguments, sizeof (arguments)) != ERROR_OK) + CORE_ESCAPE("unable to pass arguments to the thread"); + + /* + * 7) + */ + + if (task_start(task) != ERROR_OK) + CORE_ESCAPE("unable to start the task"); + + /* + * 8) + */ + + if (thread_start(thread) != ERROR_OK) + CORE_ESCAPE("unable to start the thread"); + + CORE_LEAVE(); +} diff --git a/kaneton/core/cpu/Makefile b/kaneton/core/cpu/Makefile new file mode 100644 index 0000000..8243f1a --- /dev/null +++ b/kaneton/core/cpu/Makefile @@ -0,0 +1,76 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/buckman/kaneton/kaneton/core/cpu/Makefile +# +# created julien quintard [tue jun 26 15:43:35 2007] +# updated matthieu bucchianeri [sat sep 1 12:31:39 2007] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/core/cpu + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +CPU_C := cpu.c + +CPU_O := $(CPU_C:.c=.o) + +CPU_INCLUDE := $(_CORE_INCLUDE_DIR_)/cpu.h + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(_CPU_LO_) + +$(_CPU_LO_): $(CPU_O) + $(call env_remove,$(_CPU_LO_),) + + $(call env_archive,$(_CPU_LO_),$(CPU_O),) + +clear: + $(call env_remove,$(CPU_O),) + + $(call env_remove,$(_CPU_LO_),) + + $(call env_purge,) + +prototypes: + $(call env_prototypes,$(CPU_INCLUDE),) + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(CPU_C),) + +dependencies: + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/core/cpu/cpu.c b/kaneton/core/cpu/cpu.c new file mode 100644 index 0000000..0e49ce9 --- /dev/null +++ b/kaneton/core/cpu/cpu.c @@ -0,0 +1,548 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/cpu/cpu.c + * + * created matthieu bucchianeri [sat jul 29 17:59:35 2006] + * updated julien quintard [sun jan 30 20:04:19 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * the CPU manager is mainly used by the scheduler to perform efficient + * CPU load balancing when it comes to SMP - Symmetric Multi-Processing. + * + * the scheduler must communicate to the CPU manager the statistics + * regarding the usage of each CPU. + * + * then, every time the cpu_balance() function is called, the statistics + * are compared and the more loaded CPUs give away some tasks to the less + * loaded ones. the cpu_migrate() function is used when such an event + * occurs, migrating a task and its threads to a specific CPU. + * + * the cpu_select() function is called by the task manager to assign a + * CPU to a task. like the load balancing algorithm, the selection should + * take account of the CPUs loads. + * + * [XXX:improvements] implement the cpu_balance() function based on the + * CPUs' statistics. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * include the machine-specific definitions required by the core. + */ + +machine_include(cpu); + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the init structure. + */ + +extern s_init* _init; + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the cpu manager. + */ + +m_cpu _cpu; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function shows a given CPU's attributes. + * + * steps: + * + * 1) retrieve the CPU object. + * 2) display the CPU attributes. + * 3) call the machine. + */ + +t_error cpu_show(i_cpu id, + mt_margin margin) +{ + o_cpu* o; + + /* + * 1) + */ + + if (cpu_get(id, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the CPU object"); + + /* + * 2) + */ + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + "CPU:\n", + MODULE_CONSOLE_MARGIN_VALUE(margin)); + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " core: id(%qd) efficiency(%qu)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + o->id, + o->efficiency); + + /* + * 3) + */ + + if (machine_call(cpu, show, id, margin) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function dumps the CPU manager. + * + * steps: + * + * 1) display general information. + * 2) retrieve the size of the set of CPUs. + * 3) show all the CPU objects. + * 4) call the machine. + */ + +t_error cpu_dump(void) +{ + o_cpu* data; + t_setsz size; + t_state st; + s_iterator i; + + /* + * 1) + */ + + module_call(console, message, + '#', "CPU manager:\n"); + + module_call(console, message, + '#', " core: cpus(%qd)\n", + _cpu.cpus); + + /* + * 2) + */ + + if (set_size(_cpu.cpus, &size) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the size of the set of CPUs"); + + /* + * 3) + */ + + module_call(console, message, + '#', " cpus: id(%qd) size(%qd)\n", + _cpu.cpus, + size); + + set_foreach(SET_OPTION_FORWARD, _cpu.cpus, &i, st) + { + if (set_object(_cpu.cpus, i, (void**)&data) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the CPU identifier"); + + if (cpu_show(data->id, + 3 * MODULE_CONSOLE_MARGIN_SHIFT) != ERROR_OK) + CORE_ESCAPE("unable to show the CPU"); + } + + /* + * 4) + */ + + if (machine_call(cpu, dump) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function retrieves the current CPU identifier. + * + * note that this functionality is left to the machine to implement. + * + * 0) verify the arguments. + * 1) call the machine. + */ + +t_error cpu_current(i_cpu* id) +{ + /* + * 0) + */ + + if (id == NULL) + CORE_ESCAPE("the 'id' argument is null"); + + /* + * 1) + */ + + if (machine_call(cpu, current, id) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function returns true if the kaneton kernel evolves in a + * multiprocessor environment. + * + * steps: + * + * 1) retrieve the size of the set of CPUs. + * 2) return false if there is a single CPU, true otherwise. + */ + +t_error cpu_multiprocessor(void) +{ + t_setsz size; + + /* + * 1) + */ + + assert(set_size(_cpu.cpus, &size) == ERROR_OK); + + /* + * 2) + */ + + if (size == 1) + CORE_FALSE(); + + CORE_TRUE(); +} + +/* + * this function returns the identifier of the CPU which has been used + * the smallest portion of time. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the size of the set of CPUs. + * 2) select a CPU. + * 3) call the machine. + */ + +t_error cpu_select(i_cpu* id) +{ + static i_cpu current = 0; + t_setsz size; + + /* + * 0) + */ + + if (id == NULL) + CORE_ESCAPE("the 'id' argument is null"); + + /* + * 1) + */ + + if (set_size(_cpu.cpus, &size) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the size of the set of CPUs"); + + /* + * 2) + */ + + *id = current++ % size; + + /* + * 3) + */ + + if (machine_call(cpu, select, id) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function updates the statistics of a given CPU. + * + * steps: + * + * 1) retrieve the CPU object. + * 2) update the CPU statistics. + */ + +t_error cpu_update(i_cpu id, + t_timeslice timeslice) +{ + o_cpu* o; + + /* + * 1) + */ + + if (cpu_get(id, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the CPU object"); + + /* + * 2) + */ + + o->efficiency += timeslice; + + /* + * 3) + */ + + if (machine_call(cpu, update, id, timeslice) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function load-balance the tasks among the CPUs according to the + * statistics recorded. + * + * steps: + * + * 1) call the machine. + */ + +t_error cpu_balance(void) +{ + /* + * 1) + */ + + if (machine_call(cpu, balance) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function is used to move a task from one CPU to another. + * + * steps: + * + * 1) retrieve the task object. + * 2) if the task is running, stop it the time for it to be migrated. + * 3) change the CPU. + * 4) call the machine. + * 5) is the task was running, re-start it. + */ + +t_error cpu_migrate(i_task task, + i_cpu cpu) +{ + o_task* o; + t_state state; + + /* + * 1) + */ + + if (task_get(task, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + state = o->state; + + if (o->state == TASK_STATE_START) + { + if (task_stop(task) != ERROR_OK) + CORE_ESCAPE("unable to stop the task"); + } + + /* + * 3) + */ + + o->cpu = cpu; + + /* + * 4) + */ + + if (machine_call(cpu, migrate, task, cpu) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 5) + */ + + if (state == TASK_STATE_START) + { + if (task_start(task) != ERROR_OK) + CORE_ESCAPE("unable to stop the task"); + } + + CORE_LEAVE(); +} + +/* + * this function returns true if the CPU object exists. + */ + +t_error cpu_exist(i_cpu id) +{ + if (set_exist(_cpu.cpus, id) != ERROR_TRUE) + CORE_FALSE(); + + CORE_TRUE(); +} + +/* + * this function retrieves a CPU object from the set. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the object from the set of CPUs. + */ + +t_error cpu_get(i_cpu id, + o_cpu** object) +{ + /* + * 0) + */ + + if (object == NULL) + CORE_ESCAPE("the 'object' argument is null"); + + /* + * 1) + */ + + if (set_get(_cpu.cpus, id, (void**)object) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the object from the set of CPUs"); + + CORE_LEAVE(); +} + +/* + * this function initializes the CPU manager. + * + * steps: + * + * 1) display a message. + * 2) initialize memory for the manager's structure. + * 3) reserve the set of CPUs. + * 4) inject every CPU object provided by the boot loader through the + * init structure into the set of CPUs. + * 5) call the machine. + */ + +t_error cpu_initialize(void) +{ + t_uint32 i; + + /* + * 1) + */ + + module_call(console, message, + '+', "initializing the CPU manager\n"); + + /* + * 2) + */ + + memset(&_cpu, 0x0, sizeof (m_cpu)); + + /* + * 3) + */ + + if (set_reserve(array, + SET_OPTION_ALLOCATE, + _init->ncpus, + sizeof (o_cpu), + &_cpu.cpus) != ERROR_OK) + CORE_ESCAPE("unable to reserve the set of CPUs"); + + /* + * 4) + */ + + for (i = 0; i < _init->ncpus; i++) + { + if (set_append(_cpu.cpus, &_init->cpus[i]) != ERROR_OK) + CORE_ESCAPE("unable to add the object to the set of CPUs"); + } + + if (i == 1) + module_call(console, message, + '#', " mode: mono-processor\n"); + else + module_call(console, message, + '#', " mode: multi-processor\n"); + + /* + * 5) + */ + + if (machine_call(cpu, initialize) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function cleans the CPU manager. + * + * steps: + * + * 1) display a message. + * 2) call the machine. + */ + +t_error cpu_clean(void) +{ + /* + * 1) + */ + + module_call(console, message, + '+', "cleaning the CPU manager\n"); + + /* + * 2) + */ + + if (machine_call(cpu, clean) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} diff --git a/kaneton/core/event/Makefile b/kaneton/core/event/Makefile new file mode 100644 index 0000000..61fb039 --- /dev/null +++ b/kaneton/core/event/Makefile @@ -0,0 +1,76 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/buckman/kaneton/kaneton/core/event/Makefile +# +# created julien quintard [thu jun 28 20:32:08 2007] +# updated matthieu bucchianeri [sat sep 1 12:31:13 2007] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/core/event + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +EVENT_C := event.c + +EVENT_O := $(EVENT_C:.c=.o) + +EVENT_INCLUDE := $(_CORE_INCLUDE_DIR_)/event.h + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(_EVENT_LO_) + +$(_EVENT_LO_): $(EVENT_O) + $(call env_remove,$(_EVENT_LO_),) + + $(call env_archive,$(_EVENT_LO_),$(EVENT_O),) + +clear: + $(call env_remove,$(EVENT_O),) + + $(call env_remove,$(_EVENT_LO_),) + + $(call env_purge,) + +prototypes: + $(call env_prototypes,$(EVENT_INCLUDE),) + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(EVENT_C),) + +dependencies: + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/core/event/event.c b/kaneton/core/event/event.c new file mode 100644 index 0000000..c5b8ffd --- /dev/null +++ b/kaneton/core/event/event.c @@ -0,0 +1,574 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/event/event.c + * + * created renaud voltz [sun feb 12 23:04:54 2006] + * updated julien quintard [wed mar 2 20:26:12 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * the event managers provides the possibility to generate a notification + * every time a given hardware event occurs. + * + * note that the event identifiers are machine-specific. therefore, such + * events should not be reserved from the core since such events may make + * no sense on another machine. + * + * the notification can be delivered in two forms. either a function can be + * directly called, form which is especially useful for the kernel or a + * message can be sent to a task. this last form of notification is interesting + * should a server be interested in receiving specific hardware events. for + * example, the ATA server may be interested in being notified of every + * hardware interrupt related to the ATA devices. + * + * [XXX:improvements] provide the possibility for multiple handlers on a + * the same event. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * include the machine-specific definitions required by the core. + */ + +machine_include(event); + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the kernel manager. + */ + +extern m_kernel _kernel; + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the event manager. + */ + +m_event _event; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function shows a given event's attributes. + * + * steps: + * + * 1) retrieve the event object. + * 2) display the event's attributes depending on the type of notification. + * 3) call the machine. + */ + +t_error event_show(i_event id, + mt_margin margin) +{ + o_event* o; + + /* + * 1) + */ + + if (event_get(id, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the event object"); + + /* + * 2) + */ + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + "event:\n", + MODULE_CONSOLE_MARGIN_VALUE(margin)); + + switch (o->type) + { + case EVENT_TYPE_FUNCTION: + { + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " core: id(%qd) type(function) routine(0x%x)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + o->id, + o->handler.routine); + + break; + } + case EVENT_TYPE_MESSAGE: + { + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " core: id(%qd) type(message) task(%qd)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + o->id, + o->handler.task); + + break; + } + default: + CORE_ESCAPE("unknown event type '%u'", + o->type); + } + + /* + * 3) + */ + + if (machine_call(event, show, id, margin) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function dumps the event manager. + * + * steps: + * + * 1) display general information. + * 2) retrieve the number of registered events. + * 3) show every event. + * 4) call the machine. + */ + +t_error event_dump(void) +{ + o_event* data; + t_setsz size; + t_state state; + s_iterator i; + + /* + * 1) + */ + + module_call(console, message, + '#', "event manager:\n", + _event.events); + + module_call(console, message, + '#', " core: events(%qd)\n", + _event.events); + + /* + * 2) + */ + + if (set_size(_event.events, &size) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the size of the set of events"); + + /* + * 3) + */ + + module_call(console, message, + '#', " events: id(%qd) size(%qd)\n", + _event.events, + size); + + set_foreach(SET_OPTION_FORWARD, _event.events, &i, state) + { + if (set_object(_event.events, i, (void**)&data) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the object from the set of events"); + + if (event_show(data->id, + 3 * MODULE_CONSOLE_MARGIN_SHIFT) != ERROR_OK) + CORE_ESCAPE("unable to show the object"); + } + + /* + * 4) + */ + + if (machine_call(event, dump) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * notify a task that an event occured. + * + * steps: + * + * 1) retrieve the event object. + * 2) notify the target of the event depending on the type of notification + * set up at the reservation. + * a) call the routine, taking to pass both the event identifier and + * the data provided at the reservation. + * b) send a message to the target task + * c) return an error since the type of notification is not supported. + * 3) call the machine. + */ + +t_error event_notify(i_event id) +{ + o_event* o; + + /* + * 1) + */ + + if (event_get(id, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the event object"); + + /* + * 2) + */ + + switch (o->type) + { + /* + * a) + */ + + case EVENT_TYPE_FUNCTION: + { + o->handler.routine(o->id, o->data); + + break; + } + + /* + * b) + */ + + case EVENT_TYPE_MESSAGE: + { + o_event_message message; + i_node node; + + message.id = id; + message.data = o->data; + + node.cell = _kernel.cell; + node.task = o->handler.task; + + if (message_send(_kernel.task, + node, + MESSAGE_TYPE_EVENT, + (t_vaddr)&message, + sizeof (o_event_message)) != ERROR_OK) + CORE_ESCAPE("unable to send a message to the destination task"); + + break; + } + + /* + * c) + */ + + default: + CORE_ESCAPE("unknown event type '%u'", + o->type); + } + + /* + * 3) + */ + + if (machine_call(event, notify, id) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function activates the event processing. note that this operation + * is left to the machine. + * + * steps: + * + * 1) call the machine. + */ + +t_error event_enable(void) +{ + /* + * 1) + */ + + if (machine_call(event, enable) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function deactivates the event processing. note that this operation + * is left to the machine. + * + * steps: + * + * 1) call the machine. + */ + +t_error event_disable(void) +{ + /* + * 1) + */ + + if (machine_call(event, disable) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function reserves an event that must be notified by the given + * method: intra-kernel function call or via a message. + * + * note that the macro-functions EVENT_TYPE_FUNCTION/EVENT_TYPE_MESSAGE + * and EVENT_ROUTINE()/EVENT_TASK() are provide to make reservation + * easier. + * + * steps: + * + * 0) verify the arguments. + * 1) check that the event has not been already reserved. + * 2) create and initialize a new event object. + * 3) add the object to the set of events. + * 4) call the machine. + */ + +t_error event_reserve(i_event id, + t_type type, + u_event_handler handler, + t_data data) +{ + o_event o; + + /* + * 0) + */ + + if ((type != EVENT_TYPE_FUNCTION) && + (type != EVENT_TYPE_MESSAGE)) + CORE_ESCAPE("unknown event type '%u'", + type); + + /* + * 1) + */ + + if (event_exist(id) == ERROR_TRUE) + CORE_ESCAPE("this event has already been reserved"); + + /* + * 2) + */ + + memset(&o, 0x0, sizeof (o_event)); + + o.id = id; + o.type = type; + o.handler = handler; + o.data = data; + + /* + * 3) + */ + + if (set_add(_event.events, &o) != ERROR_OK) + CORE_ESCAPE("unable to add the object to the set of events"); + + /* + * 4) + */ + + if (machine_call(event, reserve, id, type, handler, data) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function releases an event. + * + * steps: + * + * 1) call the machine. + * 2) remove the object from the set of events. + */ + +t_error event_release(i_event id) +{ + /* + * 1) + */ + + if (machine_call(event, release, id) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 2) + */ + + if (set_remove(_event.events, id) != ERROR_OK) + CORE_ESCAPE("unable to remove the object from the set of events"); + + CORE_LEAVE(); +} + +/* + * this function returns true if the event exists i.e has been reserved. + */ + +t_error event_exist(i_event id) +{ + if (set_exist(_event.events, id) != ERROR_TRUE) + CORE_FALSE(); + + CORE_TRUE(); +} + +/* + * this function retrieves the object from the set of events. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the object from the set. + */ + +t_error event_get(i_event id, + o_event** object) +{ + /* + * 0) + */ + + if (object == NULL) + CORE_ESCAPE("the 'object' argument is null"); + + /* + * 1) + */ + + if (set_get(_event.events, id, (void**)object) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the event object from the set"); + + CORE_LEAVE(); +} + +/* + * this function initializes the event manager. + * + * steps: + * + * 1) display a message. + * 2) initialize the event manager's structure. + * 3) reserve the set of events. + * 4) call the machine. + */ + +t_error event_initialize(void) +{ + /* + * 1) + */ + + module_call(console, message, + '+', "initializing the event manager\n"); + + /* + * 2) + */ + + memset(&_event, 0x0, sizeof (m_event)); + + /* + * 3) + */ + + if (set_reserve(ll, + SET_OPTION_ALLOCATE | SET_OPTION_SORT, + sizeof (o_event), + &_event.events) != ERROR_OK) + CORE_ESCAPE("unable to reserve the set of events"); + + /* + * 4) + */ + + if (machine_call(event, initialize) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function cleans the event manager. + * + * steps: + * + * 1) display a message. + * 2) call the machine. + * 3) release every event object. + * 4) release the set of events. + */ + +t_error event_clean(void) +{ + s_iterator i; + o_event* o; + + /* + * 1) + */ + + module_call(console, message, + '+', "cleaning the event manager\n"); + + /* + * 2) + */ + + if (machine_call(event, clean) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 3) + */ + + while (set_head(_event.events, &i) == ERROR_TRUE) + { + if (set_object(_event.events, i, (void**)&o) != ERROR_OK) + CORE_ESCAPE("unable to find the event object corresponding to " + "its identifier"); + + if (event_release(o->id) != ERROR_OK) + CORE_ESCAPE("unable to release the event object"); + } + + /* + * 4) + */ + + if (set_release(_event.events) != ERROR_OK) + CORE_ESCAPE("unable to release the set of events"); + + CORE_LEAVE(); +} diff --git a/kaneton/core/id/Makefile b/kaneton/core/id/Makefile new file mode 100644 index 0000000..fdb1a7b --- /dev/null +++ b/kaneton/core/id/Makefile @@ -0,0 +1,76 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/buckman/kaneton/kaneton/core/id/Makefile +# +# created julien quintard [thu jun 28 20:32:29 2007] +# updated matthieu bucchianeri [sat sep 1 12:31:01 2007] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/core/id + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +ID_C := id.c + +ID_O := $(ID_C:.c=.o) + +ID_INCLUDE := $(_CORE_INCLUDE_DIR_)/id.h + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(_ID_LO_) + +$(_ID_LO_): $(ID_O) + $(call env_remove,$(_ID_LO_),) + + $(call env_archive,$(_ID_LO_),$(ID_O),) + +clear: + $(call env_remove,$(ID_O),) + + $(call env_remove,$(_ID_LO_),) + + $(call env_purge,) + +prototypes: + $(call env_prototypes,$(ID_INCLUDE),) + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(ID_C),) + +dependencies: + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/core/id/id.c b/kaneton/core/id/id.c new file mode 100644 index 0000000..1b3182d --- /dev/null +++ b/kaneton/core/id/id.c @@ -0,0 +1,222 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/id/id.c + * + * created julien quintard [fri feb 11 03:04:40 2005] + * updated julien quintard [sun jan 30 20:06:28 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * the identifier manager provides functions for managing identifier objects + * which are capable to generate identifiers used to identify the + * different kaneton objects. + * + * note that the kaneton kernel uses 64-bit identifier and that every + * manager relies on its specific identifier object meaning that the + * identifier, say 42, can be used for a task, a thread, a segment and so + * forth. therefore an identifier alone is meaningless as it must always + * be known which manager it belongs to. + * + * the use of manager-specific identifier objects and 64-bit identifiers + * imply that identifiers do not need to be recycled. + * + * finally, note that unlike other managers, the identifier manager does + * not rely on an hypothetical machine complementary implementation since + * this manager is at the heart of the kaneton microkernel. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the identifier manager structure. + */ + +m_id _id; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function shows the given object's attributes and state. + */ + +t_error id_show(o_id* object, + mt_margin margin) +{ + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + "identifier: id(%qd)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + object->id); + + CORE_LEAVE(); +} + +/* + * this function generates an identifier from the given identifier object. + * + * steps: + * + * 0) verify the arguments. + * 1) generate the identifier. + */ + +t_error id_reserve(o_id* object, + t_id* id) +{ + /* + * 0) + */ + + if (object == NULL) + CORE_ESCAPE("the 'object' argument is null"); + + if (id == NULL) + CORE_ESCAPE("the 'id' argument is null"); + + /* + * 1) + */ + + *id = object->id++; + + CORE_LEAVE(); +} + +/* + * this function releases an identifier from an identifier object. + * + * note that since the identifiers are not recycled, there is nothing to + * do here. + * + * steps: + * + * 0) verify the arguments. + */ + +t_error id_release(o_id* object, + t_id id) +{ + /* + * 0) + */ + + if (object == NULL) + CORE_ESCAPE("the 'object' argument is null"); + + CORE_LEAVE(); +} + +/* + * this function builds an identifier object. + * + * steps: + * + * 0) verify the arguments + * 1) initialize the identifier object's structure. + */ + +t_error id_build(o_id* object) +{ + /* + * 0) + */ + + if (object == NULL) + CORE_ESCAPE("the 'object' argument is null"); + + /* + * 1) + */ + + memset(object, 0x0, sizeof (o_id)); + + CORE_LEAVE(); +} + +/* + * this function destroys an identifier object. + * + * 0) verify the arguments. + * 1) re-initialize the object's structure. + */ + +t_error id_destroy(o_id* object) +{ + /* + * 0) + */ + + if (object == NULL) + CORE_ESCAPE("the 'object' argument is null"); + + /* + * 1) + */ + + memset(object, 0x0, sizeof (o_id)); + + CORE_LEAVE(); +} + +/* + * this function initialize the identifier manager. + * + * steps: + * + * 1) display a message. + * 2) initialize the manager's structure. + */ + +t_error id_initialize(void) +{ + /* + * 1) + */ + + module_call(console, message, + '+', "initializing the identifier manager\n"); + + /* + * 2) + */ + + memset(&_id, 0x0, sizeof (m_id)); + + CORE_LEAVE(); +} + +/* + * this function cleans the identifier manager. + * + * 1) display a message. + */ + +t_error id_clean(void) +{ + /* + * 1) + */ + + module_call(console, message, + '+', "cleaning the identifier manager\n"); + + CORE_LEAVE(); +} diff --git a/kaneton/core/include/as.h b/kaneton/core/include/as.h new file mode 100644 index 0000000..6581395 --- /dev/null +++ b/kaneton/core/include/as.h @@ -0,0 +1,165 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/include/as.h + * + * created julien quintard [wed jun 6 12:25:01 2007] + * updated julien quintard [fri dec 10 21:16:27 2010] + */ + +#ifndef CORE_AS_H +#define CORE_AS_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include +#include +#include + +#include + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * init sizes for the array data structure set + */ + +#define AS_SEGMENTS_INITSZ 0x4 +#define AS_REGIONS_INITSZ 0x4 + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * as object + */ + +typedef struct +{ + i_as id; + + i_task task; + + i_set segments; + i_set regions; + + machine_data(o_as); +} o_as; + +/* + * as manager + */ + +typedef struct +{ + o_id id; + + + i_set ass; + + machine_data(m_as); +} m_as; + +/* + * the address space dispatcher. + */ + +typedef struct +{ + t_error (*as_show)(i_as, + mt_margin); + t_error (*as_dump)(void); + t_error (*as_virtual)(i_as, + t_paddr, + t_vaddr*); + t_error (*as_physical)(i_as, + t_vaddr, + t_paddr*); + t_error (*as_read)(i_as, + t_vaddr, + t_vsize, + void*); + t_error (*as_write)(i_as, + const void*, + t_vaddr, + t_vsize); + t_error (*as_copy)(i_as, + t_vaddr, + i_as, + t_vaddr, + t_vsize); + t_error (*as_reserve)(i_task, + i_as*); + t_error (*as_release)(i_as); + t_error (*as_initialize)(void); + t_error (*as_clean)(void); +} d_as; + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../../core/as/as.c + */ + +/* + * ../../core/as/as.c + */ + +t_error as_show(i_as id, + mt_margin margin); + +t_error as_dump(void); + +t_error as_virtual(i_as id, + t_paddr physical, + t_vaddr* virtual); + +t_error as_physical(i_as id, + t_vaddr virtual, + t_paddr* physical); + +t_error as_read(i_as id, + t_vaddr source, + t_vsize size, + void* destination); + +t_error as_write(i_as id, + const void* source, + t_vsize size, + t_vaddr destination); + +t_error as_copy(i_as source_id, + t_vaddr source_address, + i_as destination_id, + t_vaddr destination_address, + t_vsize size); + +t_error as_reserve(i_task task, + i_as* id); + +t_error as_release(i_as id); + +t_error as_exist(i_as id); + +t_error as_get(i_as id, + o_as** object); + +t_error as_initialize(void); + +t_error as_clean(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/core/include/bpt.h b/kaneton/core/include/bpt.h new file mode 100644 index 0000000..a9faf59 --- /dev/null +++ b/kaneton/core/include/bpt.h @@ -0,0 +1,3138 @@ +/* + * bpt.h + * + * bpt.h + * + * made by mycure + * quintard julien [quinta_j@epita.fr] + */ + +/* + * copyright EPITA System Laboratory + * + * http://www.lse.epita.fr + */ + +#ifndef BPT_H +#define BPT_H + +/* + * includes + */ + +#if 0 +#ifndef $kaneton +#include +#include +#endif +#endif + +/* + * general defines + */ + +#define BPT_VERSION "2.3" + +/* + * init values and limitations + */ + +#define BPT_INIT_HEIGHT 0x1 +#define BPT_INIT_NIKEYS 0x2 +#define BPT_INIT_NLKEYS 0x2 + +/* + * default types + */ + +#define BPT_NDI_T signed int +#define BPT_UNI_T signed int +#define BPT_OPTS_T unsigned char +#define BPT_CB_T unsigned char +#define BPT_TYPE_T unsigned char +#define BPT_FLAGS_T unsigned char +#define BPT_NODESZ_T unsigned int +#define BPT_NODES_T unsigned int +#define BPT_HEIGHT_T unsigned short + +/* + * general types + */ + +typedef BPT_CB_T t_bpt_cb; +typedef BPT_OPTS_T t_bpt_opts; +typedef BPT_TYPE_T t_bpt_type; +typedef BPT_FLAGS_T t_bpt_flags; + +/* + * options + */ + +#define BPT_OPT_ZERO 0x0 + +#define BPT_OPT_TREE 0x1 +#define BPT_OPT_NODE 0x2 + +#define BPT_OPT_HEAD 0x1 +#define BPT_OPT_TAIL 0x2 + +#define BPT_OPT_KEY 0x1 +#define BPT_OPT_PARENT 0x2 + +#define BPT_OPT_PERFECT 0x1 +#define BPT_OPT_PARTIAL 0x2 +#define BPT_OPT_COLLIDE 0x4 + +#define BPT_OPT_NONDI(T) (t_bpt_ndi_##T)0x0 +#define BPT_OPT_NOKEY(T) (t_bpt_key_##T)0x0 + +#define BPT_OPT_INIT 0x1 +#define BPT_OPT_ADD 0x2 +#define BPT_OPT_MODIFY 0x3 +#define BPT_OPT_REMOVE 0x4 +#define BPT_OPT_CLEAN 0x5 + +/* + * callbacks + */ + +#define BPT_CB_UNKNOWN 0x0 +#define BPT_CB_INSERT 0x1 +#define BPT_CB_SPLIT 0x2 +#define BPT_CB_MODIFY 0x3 +#define BPT_CB_DELETE 0x4 +#define BPT_CB_MIGRATE 0x5 +#define BPT_CB_BALANCE 0x6 + +/* + * flags + */ + +#define BPT_FLAG_ZERO 0x0 +#define BPT_FLAG_CALLBACK 0x1 +#define BPT_FLAG_COLLIDE 0x2 + +/* + * types + */ + +#define BPT_TYPE_INTERNAL 0x01 +#define BPT_TYPE_LEAF 0x02 + +/* + * debug + */ + +#define bpt_debug(_stream_, _fmt_...) + +/* + * macros that indicate the number of array's entries the programmer has to + * fill with blocks addresses before performing common tree's operations + * such as: init, add, modify ... + * + * for the remove operations, no blocks addresses are needed but an array + * has to be passed, because the bpt library will fill the entries with + * the addresses of the blocks that are now unused. + * + * for the init operation, a block is needed for creating the root node. + * + * the clean operation doest no use any allocation but need an array + */ + +#define BPT_INIT_ALLOC() \ + (1) + +#define BPT_ADD_ALLOC(_bpt_) \ + ((_bpt_)->height + 1) + +#define BPT_MODIFY_ALLOC(_bpt_) \ + (BPT_ADD_ALLOC(_bpt_) + BPT_REMOVE_ALLOC(_bpt_)) + +#define BPT_REMOVE_ALLOC(_bpt_) \ + (0) + +#define BPT_CLEAN_ALLOC(_bpt_) \ + (0) + +/* + * macros that specify the size of the array the bpt library need as + * parameter + */ + +#define BPT_INIT_SIZE() \ + (1) + +#define BPT_ADD_SIZE(_bpt_) \ + ((_bpt_)->height + 1) + +#define BPT_MODIFY_SIZE(_bpt_) \ + (BPT_ADD_SIZE(_bpt_) + BPT_REMOVE_SIZE(_bpt_)) + +#define BPT_REMOVE_SIZE(_bpt_) \ + (_bpt_)->height + +#define BPT_CLEAN_SIZE(_bpt_) \ + ((_bpt_)->nodes) + +/* + * user friendly macros + */ + +#define t_bpt(T) \ + t_bpt_##T + +#define t_bpt_addr(T) \ + t_bpt_addr_##T + +#define t_bpt_key(T) \ + t_bpt_key_##T + +#define t_bpt_value(T) \ + t_bpt_value_##T + +#define t_bpt_ndi(T) \ + t_bpt_ndi_##T + +#define t_bpt_uni(T) \ + t_bpt_uni_##T + +#define t_bpt_nodesz(T) \ + t_bpt_nodesz_##T + +#define t_bpt_nodes(T) \ + t_bpt_nodes_##T + +#define t_bpt_height(T) \ + t_bpt_height_##T + +#define t_bpt_node(T) \ + t_bpt_node_##T + +#define t_bpt_imm(T) \ + t_bpt_imm_##T + +#define t_bpt_cbctx(T) \ + t_bpt_cbctx_##T + +#define t_bpt_unused(T) \ + t_bpt_unused_##T + +#define t_bpt_head(T) \ + t_bpt_head_##T + +#define t_bpt_inentry(T) \ + t_bpt_inentry_##T + +#define t_bpt_lfentry(T) \ + t_bpt_lfentry_##T + +#define t_bpt_entry(T) \ + t_bpt_entry_##T + +#define t_bpt_load_fn(T) \ + t_bpt_load_fn_##T + +#define t_bpt_unload_fn(T) \ + t_bpt_unload_fn_##T + +#define t_bpt_addrcmp_fn(T) \ + t_bpt_addrcmp_fn_##T + +#define t_bpt_keycmp_fn(T) \ + t_bpt_keycmp_fn_##T + +#define t_bpt_valcmp_fn(T) \ + t_bpt_valcmp_fn_##T + +#define t_bpt_cb_fn(T) \ + t_bpt_cb_fn_##T + +/* + * bpt traps + */ + +#define bpt_foreach(T, _bpt_, _entry_) \ + bpt_foreach_##T(_bpt_, _entry_) + +#define bpt_list(T, _bpt_, _node_, _entry_, _opts_) \ + bpt_list_##T(_bpt_, _node_, _entry_, _opts_) + +#define bpt_check_unused(T, _bpt_, _unused_, _opts_) \ + bpt_check_unused_##T(_bpt_, _unused_, _opts_) + +#define bpt_first_entry(T, _bpt_, _node_, _first_) \ + bpt_first_entry_##T(_bpt_, _node_, _first_) + +#define bpt_prev_entry(T, _bpt_, _current_, _previous_, _opts_) \ + bpt_prev_entry_##T(_bpt_, _current_, _previous_, _opts_) + +#define bpt_next_entry(T, _bpt_, _current_, _next_, _opts_) \ + bpt_next_entry_##T(_bpt_, _current_, _next_, _opts_) + +#define bpt_last_entry(T, _bpt_, _node_, _last_) \ + bpt_last_entry_##T(_bpt_, _node_, _last_) + +#define bpt_reinit_entries(T, _bpt_, _node_) \ + bpt_reinit_entries_##T(_bpt_, _node_) + +#define bpt_make_node(T, _bpt_, _node_, _type_) \ + bpt_make_node_##T(_bpt_, _node_, _type_) + +#define bpt_key(T, _bpt_, _node_, _key_) \ + bpt_key_##T(_bpt_, _node_, _key_) + +#define bpt_ndi(T, _bpt_, _node_, _value_, _ndi_) \ + bpt_ndi_##T(_bpt_, _node_, _value_, _ndi_) + +#define bpt_update_node(T, _bpt_, _node_, _addr_, _key_,_opts_) \ + bpt_update_node_##T(_bpt_, _node_, _addr_, _key_, _opts_) + +#define bpt_update_parent(T, _bpt_, _node_) \ + bpt_update_parent_##T(_bpt_, _node_) + +#define bpt_update(T, _bpt_, _node_, _opts_) \ + bpt_update_##T(_bpt_, _node_, _opts_) + +#define bpt_linear_search(T, _bpt_, _node_, _key_, _ndi_, _opts_) \ + bpt_linear_search_##T(_bpt_, _node_, _key_, _ndi_, _opts_) + +#define bpt_dichotomic_search(T, _bpt_, _node_, _key_, _ndi_, _opts_) \ + bpt_dichotomic_search_##T(_bpt_, _node_, _key_, _ndi_, _opts_) + +#define bpt_search_entry(T, _bpt_, _node_, _key_, _ndi_, _opts_) \ + bpt_search_entry_##T(_bpt_, _node_, _key_, _ndi_, _opts_) + +#define bpt_search_leaf(T, _bpt_, _node_, _leaf_, _key_) \ + bpt_search_leaf_##T(_bpt_, _node_, _leaf_, _key_) + +#define bpt_search(T, _bpt_, _key_, _entry_) \ + bpt_search_##T(_bpt_, _key_, _entry_) + +#define bpt_collide_next(T, _bpt_, _key_, _entry_) \ + bpt_collide_next_##T(_bpt_, _key_, _entry_) + +#define bpt_collide_search(T, _bpt_, _key_, _value_, _entry_) \ + bpt_collide_search_##T(_bpt_, _key_, _value_, _entry_) + +#define bpt_check_collide(T, _bpt_, _node1_, _key_, _value_) \ + bpt_check_collide_##T(_bpt_, _node1_, _key_, _value_) + +#define bpt_node_size(T, _bpt_, _node_) \ + bpt_node_size_##T(_bpt_, _node_) + +#define bpt_simplify(T, _bpt_, _node_, _unused_) \ + bpt_simplify_##T(_bpt_, _node_, _unused_) + +#define bpt_balancein_1(T, _bpt_, _node1_, _node2_, \ + _cbctx_, _prev_, _unused_) \ + bpt_balancein_1_##T(_bpt_, _node1_, _node2_, _cbctx_, _prev_, _unused_) + +#define bpt_balancein_2(T, _bpt_, _node1_, _node2_, \ + _cbctx_, _prev_, _unused_) \ + bpt_balancein_2_##T(_bpt_, _node1_, _node2_, _cbctx_, _prev_, _unused_) + +#define bpt_balancein_3(T, _bpt_, _node1_, _node2_, \ + _cbctx_, _prev_, _unused_) \ + bpt_balancein_3_##T(_bpt_, _node1_, _node2_, _cbctx_, _prev_, _unused_) + +#define bpt_balancein_4(T, _bpt_, _node1_, _node2_, \ + _cbctx_, _prev_, _unused_) \ + bpt_balancein_4_##T(_bpt_, _node1_, _node2_, _cbctx_, _prev_, _unused_) + +#define bpt_delete(T, _bpt_, _entry_, _cbctx_, _unused_) \ + bpt_delete_##T(_bpt_, _entry_, _cbctx_, _unused_) + +#define bpt_remove(T, _bpt_, _key_, _unused_) \ + bpt_remove_##T(_bpt_, _key_, _unused_) + +#define bpt_collide_remove(T, _bpt_, _entry_, _unused_) \ + bpt_collide_remove_##T(_bpt_, _entry_, _unused_) + +#define bpt_modify(T, _bpt_, _key_, _lfentry_, _unused_) \ + bpt_modify_##T(_bpt_, _key_, _lfentry_, _unused_) + +#define bpt_collide_modify(T, _bpt_, _entry_, _lfentry_, _unused_) \ + bpt_collide_modify_##T(_bpt_, _entry_, _lfentry_, _unused_) + +#define bpt_insert_head(T, _bpt_, _node1_, _node2_) \ + bpt_insert_head_##T(_bpt_, _node1_, _node2_) + +#define bpt_insert_tail(T, _bpt_, _node1_, _node2_) \ + bpt_insert_tail_##T(_bpt_, _node1_, _node2_) + +#define bpt_shift_sort(T, _bpt_, _node_) \ + bpt_shift_sort_##T(_bpt_, _node_) + +#define bpt_insert_sort(T, _bpt_, _node_, _key_, _ndi_, _opts_) \ + bpt_insert_sort_##T(_bpt_, _node_, _key_, _ndi_, _opts_) + +#define bpt_new_root(T, _bpt_, _node1_, _node2_, _unused_) \ + bpt_new_root_##T(_bpt_, _node1_, _node2_, _unused_) + +#define bpt_balanceout(T, _bpt_, _node1_, _node2_, _entry_, _cbctx_) \ + bpt_balanceout_##T(_bpt_, _node1_, _node2_, _entry_, _cbctx_) + +#define bpt_split_node(T, _bpt_, _node1_, _entry_, \ + _current_, _unused_) \ + bpt_split_node_##T(_bpt_, _node1_, _entry_, _current_, _unused_) + +#define bpt_insert(T, _bpt_, _node_, _entry_, _cbctx_, _unused_) \ + bpt_insert_##T(_bpt_, _node_, _entry_, _cbctx_, _unused_) + +#define bpt_add(T, _bpt_, _lfentry_, _unused_) \ + bpt_add_##T(_bpt_, _lfentry_, _unused_) + +#define bpt_init(T, _bpt_, _nodesz_, _uaddr_, _ukey_, _uval_, \ + _flags_, _load_, _unload_, _addrcmp_, _keycmp_, \ + _valcmp_, _callback_, _data_, _unused_) \ + bpt_init_##T(_bpt_, _nodesz_, _uaddr_, _ukey_, _uval_, _flags_, _load_, \ + _unload_, _addrcmp_, _keycmp_, _valcmp_, _callback_, \ + _data_, _unused_) + +#define bpt_clean_node(T, _bpt_, _node_, _unused_) \ + bpt_clean_node_##T(_bpt_, _node_, _unused_) + +#define bpt_clean(T, _bpt_, _unused_) \ + bpt_clean_##T(_bpt_, _unused_) + +/* + * macros used to manipule nodes and entries + */ + +#define BPT_GET_HEAD(T, _node_, _elem_) \ + ((t_bpt_head(T) *)((_node_)->buf))->_elem_ + +#define BPT_SET_HEAD(T, _node_, _elem_, _value_) \ + ((t_bpt_head(T) *)((_node_)->buf))->_elem_ = (_value_) + +#define BPT_INENTRY(T, _node_, _ndi_) \ + (t_bpt_inentry(T) *)((_node_)->buf + sizeof (t_bpt_head(T)) + \ + (_ndi_) * sizeof (t_bpt_inentry(T))) + +#define BPT_LFENTRY(T, _node_, _ndi_) \ + (t_bpt_lfentry(T) *)((_node_)->buf + sizeof (t_bpt_head(T)) + \ + (_ndi_) * sizeof (t_bpt_lfentry(T))) + +#define BPT_INIT_ENTRY(T, _node_, _ndi_) \ + memset(BPT_GET_HEAD(T, (_node_), type) == BPT_TYPE_INTERNAL ? \ + (_node_)->buf + sizeof (t_bpt_head(T)) + \ + (_ndi_) * sizeof (t_bpt_inentry(T)) : \ + (_node_)->buf + sizeof (t_bpt_head(T)) + \ + (_ndi_) * sizeof (t_bpt_lfentry(T)), 0x0, \ + BPT_GET_HEAD(T, (_node_), type) == BPT_TYPE_INTERNAL ? \ + sizeof (t_bpt_inentry(T)) : sizeof (t_bpt_lfentry(T))) + +#define BPT_IMPORT_ENTRY(T, _node_, _ndi_, _entry_) \ + memcpy(BPT_GET_HEAD(T, (_node_), type) == BPT_TYPE_INTERNAL ? \ + (_node_)->buf + sizeof (t_bpt_head(T)) + \ + (_ndi_) * sizeof (t_bpt_inentry(T)) : \ + (_node_)->buf + sizeof (t_bpt_head(T)) + \ + (_ndi_) * sizeof (t_bpt_lfentry(T)), \ + (_entry_), \ + BPT_GET_HEAD(T, (_node_), type) == BPT_TYPE_INTERNAL ? \ + sizeof (t_bpt_inentry(T)) : sizeof (t_bpt_lfentry(T))) + +#define BPT_IMPORT_INENTRY(T, _node_, _ndi_, _entry_) \ + memcpy((_node_)->buf + sizeof (t_bpt_head(T)) + \ + (_ndi_) * sizeof (t_bpt_inentry(T)), \ + (_entry_), \ + sizeof (t_bpt_inentry(T))) + +#define BPT_IMPORT_LFENTRY(T, _node_, _ndi_, _entry_) \ + memcpy((_node_)->buf + sizeof (t_bpt_head(T)) + \ + (_ndi_) * sizeof (t_bpt_lfentry(T)), \ + (_entry_), \ + sizeof (t_bpt_lfentry(T))) + +#define BPT_EXPORT_ENTRY(T, _node_, _ndi_, _entry_) \ + memcpy((_entry_), \ + BPT_GET_HEAD(T, (_node_), type) == BPT_TYPE_INTERNAL ? \ + (_node_)->buf + sizeof (t_bpt_head(T)) + \ + (_ndi_) * sizeof (t_bpt_inentry(T)) : \ + (_node_)->buf + sizeof (t_bpt_head(T)) + \ + (_ndi_) * sizeof (t_bpt_lfentry(T)), \ + BPT_GET_HEAD(T, (_node_), type) == BPT_TYPE_INTERNAL ? \ + sizeof (t_bpt_inentry(T)) : sizeof (t_bpt_lfentry(T))) + +#define BPT_COPY_ENTRY(T, _node1_, _node2_, _ndi1_, _ndi2_) \ + memcpy(BPT_GET_HEAD(T, (_node1_), type) == BPT_TYPE_INTERNAL ? \ + (_node2_)->buf + sizeof (t_bpt_head(T)) + \ + (_ndi2_) * sizeof (t_bpt_inentry(T)) : \ + (_node2_)->buf + sizeof (t_bpt_head(T)) + \ + (_ndi2_) * sizeof (t_bpt_lfentry(T)), \ + BPT_GET_HEAD(T, (_node1_), type) == BPT_TYPE_INTERNAL ? \ + (_node1_)->buf + sizeof (t_bpt_head(T)) + \ + (_ndi1_) * sizeof (t_bpt_inentry(T)) : \ + (_node1_)->buf + sizeof (t_bpt_head(T)) + \ + (_ndi1_) * sizeof (t_bpt_lfentry(T)), \ + BPT_GET_HEAD(T, (_node1_), type) == BPT_TYPE_INTERNAL ? \ + sizeof (t_bpt_inentry(T)) : sizeof (t_bpt_lfentry(T))) + +#define BPT_SWAP_ENTRIES(T, _node_, _ndi1_, _ndi2_) \ + { \ + if (BPT_GET_HEAD(T, (_node_), type) == BPT_TYPE_INTERNAL) \ + { \ + t_bpt_inentry(T) _swap_; \ + \ + memcpy(&_swap_, (_node_)->buf + sizeof (t_bpt_head(T)) + (_ndi1_) * \ + sizeof (t_bpt_inentry(T)), sizeof (t_bpt_inentry(T))); \ + memcpy((_node_)->buf + sizeof (t_bpt_head(T)) + (_ndi1_) * \ + sizeof (t_bpt_inentry(T)), (_node_)->buf + \ + sizeof (t_bpt_head(T)) + (_ndi2_) * \ + sizeof (t_bpt_inentry(T)), sizeof (t_bpt_inentry(T))); \ + memcpy((_node_)->buf + sizeof (t_bpt_head(T)) + (_ndi2_) * \ + sizeof (t_bpt_inentry(T)), &_swap_, \ + sizeof (t_bpt_inentry(T))); \ + } \ + else \ + { \ + t_bpt_lfentry(T) _swap_; \ + \ + memcpy(&_swap_, (_node_)->buf + sizeof (t_bpt_head(T)) + (_ndi1_) * \ + sizeof (t_bpt_lfentry(T)), sizeof (t_bpt_lfentry(T))); \ + memcpy((_node_)->buf + sizeof (t_bpt_head(T)) + (_ndi1_) * \ + sizeof (t_bpt_lfentry(T)), (_node_)->buf + \ + sizeof (t_bpt_head(T)) + (_ndi2_) * \ + sizeof (t_bpt_lfentry(T)), sizeof (t_bpt_lfentry(T))); \ + memcpy((_node_)->buf + sizeof (t_bpt_head(T)) + (_ndi2_) * \ + sizeof (t_bpt_lfentry(T)), &_swap_, \ + sizeof (t_bpt_lfentry(T))); \ + } \ + } + +#define BPT_GET_ENTRY(T, _node_, _ndi_, _elem_) \ + (BPT_GET_HEAD(T, (_node_), type) == BPT_TYPE_INTERNAL ? \ + ((t_bpt_inentry(T) *)((_node_)->buf + sizeof (t_bpt_head(T)) + (_ndi_) * \ + sizeof (t_bpt_inentry(T))))->_elem_ : \ + ((t_bpt_lfentry(T) *)((_node_)->buf + sizeof (t_bpt_head(T)) + (_ndi_) * \ + sizeof (t_bpt_lfentry(T))))->_elem_) + +#define BPT_GET_INENTRY(T, _node_, _ndi_, _elem_) \ + (BPT_INENTRY(T, (_node_), (_ndi_)))->_elem_ + +#define BPT_GET_LFENTRY(T, _node_, _ndi_, _elem_) \ + (BPT_LFENTRY(T, (_node_), (_ndi_)))->_elem_ + +#define BPT_SET_ENTRY(T, _node_, _ndi_, _elem_, _value_) \ + { \ + if (BPT_GET_HEAD(T, (_node_), type) == BPT_TYPE_INTERNAL) \ + { \ + t_bpt_inentry(T) *_entry_ = BPT_INENTRY(T, _node_, _ndi_); \ + \ + _entry_->_elem_ = (_value_); \ + } \ + else \ + { \ + t_bpt_lfentry(T) *_entry_ = BPT_LFENTRY(T, _node_, _ndi_); \ + \ + _entry_->_elem_ = (_value_); \ + } \ + } + +#define BPT_SET_INENTRY(T, _node_, _ndi_, _elem_, _value_) \ + { \ + t_bpt_inentry(T) *_entry_ = BPT_INENTRY(T, _node_, _ndi_); \ + \ + _entry_->_elem_ = (_value_); \ + } + +#define BPT_SET_LFENTRY(T, _node_, _ndi_, _elem_, _value_) \ + { \ + t_bpt_lfentry(T) *_entry_ = BPT_LFENTRY(T, _node_, _ndi_); \ + \ + _entry_->_elem_ = (_value_); \ + } + +#define BPT_GET_THIS_ENTRY(T, _type_, _ptr_, _elem_) \ + ((_type_) == BPT_TYPE_INTERNAL ? \ + ((t_bpt_inentry(T) *)(_ptr_))->_elem_ : \ + ((t_bpt_lfentry(T) *)(_ptr_))->_elem_) + +#define BPT_QUOTA(T, _bpt_, _node_) \ + (t_bpt_ndi(T)) (BPT_GET_HEAD(T, (_node_), type) == BPT_TYPE_INTERNAL ? \ + ((_bpt_)->nikeys * 50 / 100) : \ + ((_bpt_)->nlkeys * 50 / 100)) + +#define BPT_NKEYS(T, _bpt_, _node_) \ + (BPT_GET_HEAD(T, (_node_), type) == BPT_TYPE_INTERNAL ? \ + (_bpt_)->nikeys : (_bpt_)->nlkeys) + +/* + * type non-dependent macros + */ + +#define BPT_NODES(_bpt_) \ + (_bpt_)->nodes + +#define BPT_NIKEYS(_bpt_) \ + (_bpt_)->nikeys + +#define BPT_NLKEYS(_bpt_) \ + (_bpt_)->nlkeys + +#define BPT_HEIGHT(_bpt_) \ + (_bpt_)->height + +#define BPT_COPY_NODE(_bpt_, _node1_, _node2_) \ + memcpy((_node2_), (_node1_), (_bpt_)->nodesz) + +#define BPT_LOAD(_bpt_, _node_, _addr_) \ + (_bpt_)->load((_bpt_), (_node_), (_addr_)) + +#define BPT_UNLOAD(_bpt_, _node_) \ + (_bpt_)->unload((_bpt_), (_node_)); + +#define BPT_ADDRCMP(_bpt_, _addr1_, _addr2_) \ + (_bpt_)->addrcmp((_bpt_), (_addr1_), (_addr2_)) + +#define BPT_KEYCMP(_bpt_, _key1_, _key2_) \ + (_bpt_)->keycmp((_bpt_), (_key1_), (_key2_)) + +#define BPT_VALCMP(_bpt_, _val1_, _val2_) \ + (_bpt_)->valcmp((_bpt_), (_val1_), (_val2_)) + +#define BPT_CALLBACK(_bpt_, _cbctx_) \ + { \ + if ((_bpt_)->flags & BPT_FLAG_CALLBACK) \ + (_bpt_)->callback((_bpt_), (_cbctx_)); \ + } + +#define BPT_GET_ROOT(_bpt_) \ + (_bpt_)->root + +#define BPT_SET_ROOT(_bpt_, _root_) \ + (_bpt_)->root = (_root_) + +#define BPT_INIT_CBCTX(_bpt_, _cbctx_) \ + { \ + if ((_bpt_)->flags & BPT_FLAG_CALLBACK) \ + { \ + (_cbctx_)->cb = BPT_CB_UNKNOWN; \ + \ + (_cbctx_)->previous.node = (_bpt_)->uaddr; \ + (_cbctx_)->previous.ndi = 0; \ + (_cbctx_)->current.node = (_bpt_)->uaddr; \ + (_cbctx_)->current.ndi = 0; \ + \ + (_cbctx_)->node = bpt->uaddr; \ + \ + (_cbctx_)->node1 = bpt->uaddr; \ + (_cbctx_)->node2 = bpt->uaddr; \ + } \ + } + +#define BPT_SET_CBCTX(_bpt_, _type_, _cbctx_, _elem_, _value_) \ + { \ + if (((_bpt_)->flags & BPT_FLAG_CALLBACK) && \ + ((_type_) == BPT_TYPE_LEAF)) \ + (_cbctx_)->_elem_ = (_value_); \ + } + +#define BPT_RESERVE(_bpt_, _unused_, _var_) \ + { \ + (_var_) = (_unused_)->array[(_unused_)->index]; \ + (_unused_)->array[(_unused_)->index] = bpt->uaddr; \ + (_unused_)->index--; \ + (_bpt_)->nodes++; \ + } + +#define BPT_RELEASE(_bpt_, _unused_, _var_) \ + { \ + (_bpt_)->nodes--; \ + (_unused_)->index++; \ + (_unused_)->array[(_unused_)->index] = (_var_); \ + } + +/* + * the core macros that build types, prototypes and functions + */ + +#define bpt_make_types(T, _t_bpt_nodesz_, _t_bpt_ndi_, _t_bpt_uni_, \ + _t_bpt_nodes_, _t_bpt_height_, _t_bpt_addr_, \ + _t_bpt_key_, _t_bpt_value_, _t_bpt_inentry_, \ + _t_bpt_lfentry_) \ + \ +typedef struct s_bpt_##T t_bpt_##T; \ + \ +typedef _t_bpt_nodesz_ t_bpt_nodesz_##T; \ +typedef _t_bpt_ndi_ t_bpt_ndi_##T; \ +typedef _t_bpt_uni_ t_bpt_uni_##T; \ +typedef _t_bpt_nodes_ t_bpt_nodes_##T; \ +typedef _t_bpt_height_ t_bpt_height_##T; \ +typedef _t_bpt_addr_ t_bpt_addr_##T; \ +typedef _t_bpt_key_ t_bpt_key_##T; \ +typedef _t_bpt_value_ t_bpt_value_##T; \ +typedef _t_bpt_inentry_ t_bpt_inentry_##T; \ +typedef _t_bpt_lfentry_ t_bpt_lfentry_##T; \ + \ +typedef t_bpt_addr(T) t_bpt_node_##T; \ + \ +typedef struct s_bpt_imm_##T \ +{ \ + t_bpt_node(T) addr; \ + void *buf; \ +} t_bpt_imm_##T; \ + \ +typedef struct s_bpt_entry_##T \ +{ \ + t_bpt_node(T) node; \ + t_bpt_ndi(T) ndi; \ +} t_bpt_entry_##T; \ + \ +typedef struct s_bpt_cbctx_##T \ +{ \ + t_bpt_cb cb; \ + \ + t_bpt_entry(T) previous; \ + t_bpt_entry(T) current; \ + \ + t_bpt_node(T) node; \ + \ + t_bpt_node(T) node1; \ + t_bpt_node(T) node2; \ +} t_bpt_cbctx_##T; \ + \ +typedef void (*t_bpt_load_fn_##T)(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_node(T) addr); \ + \ +typedef void (*t_bpt_unload_fn_##T)(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node); \ + \ +typedef int (*t_bpt_addrcmp_fn_##T)(t_bpt(T) *bpt, \ + t_bpt_addr(T) addr1, \ + t_bpt_addr(T) addr2); \ + \ +typedef int (*t_bpt_keycmp_fn_##T)(t_bpt(T) *bpt, \ + t_bpt_key(T) key1, \ + t_bpt_key(T) key2); \ + \ +typedef int (*t_bpt_valcmp_fn_##T)(t_bpt(T) *bpt, \ + t_bpt_value(T) val1, \ + t_bpt_value(T) val2); \ + \ +typedef void (*t_bpt_cb_fn_##T)(t_bpt(T) *bpt, \ + t_bpt_cbctx(T) *cbctx); \ + \ +struct s_bpt_##T \ +{ \ + t_bpt_nodesz(T) nodesz; \ + t_bpt_nodes(T) nodes; \ + t_bpt_ndi(T) nikeys; \ + t_bpt_ndi(T) nlkeys; \ + t_bpt_height(T) height; \ + t_bpt_node(T) root; \ + t_bpt_flags flags; \ + \ + t_bpt_addr(T) uaddr; \ + t_bpt_key(T) ukey; \ + t_bpt_value(T) uval; \ + \ + t_bpt_load_fn(T) load; \ + t_bpt_unload_fn(T) unload; \ + t_bpt_addrcmp_fn(T) addrcmp; \ + t_bpt_keycmp_fn(T) keycmp; \ + t_bpt_valcmp_fn(T) valcmp; \ + t_bpt_cb_fn(T) callback; \ + \ + void *data; \ +}; \ + \ +typedef struct s_bpt_unused_##T \ +{ \ + t_bpt_node(T) *array; \ + t_bpt_uni(T) index; \ +} t_bpt_unused_##T; \ + \ +typedef struct s_bpt_head_##T \ +{ \ + t_bpt_type type; \ + t_bpt_node(T) parent; \ + t_bpt_node(T) prv; \ + t_bpt_node(T) nxt; \ +} __attribute__((packed)) t_bpt_head_##T; + +#define bpt_make_protos(T) \ + \ +int bpt_foreach_##T(t_bpt(T) *bpt, \ + t_bpt_entry(T) *entry); \ + \ +int bpt_list_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_entry(T) *entry, \ + t_bpt_opts opts); \ + \ +int bpt_check_unused_##T(t_bpt(T) *bpt, \ + t_bpt_unused(T) *unused, \ + t_bpt_opts opts); \ + \ +int bpt_first_entry_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_ndi(T) *first); \ + \ +int bpt_prev_entry_##T(t_bpt(T) *bpt, \ + t_bpt_entry(T) current, \ + t_bpt_entry(T) *previous, \ + t_bpt_opts opts); \ + \ +int bpt_next_entry_##T(t_bpt(T) *bpt, \ + t_bpt_entry(T) current, \ + t_bpt_entry(T) *next, \ + t_bpt_opts opts); \ + \ +int bpt_last_entry_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_ndi(T) *last); \ + \ +void bpt_reinit_entries_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node); \ + \ +void bpt_make_node_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_type type); \ + \ +void bpt_key_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_key(T) *key); \ + \ + \ +int bpt_ndi_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_addr(T) value, \ + t_bpt_ndi(T) *ndi); \ + \ +int bpt_update_node_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_node(T) addr, \ + t_bpt_key(T) key, \ + t_bpt_opts opts); \ + \ +void bpt_update_parent_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node); \ + \ +int bpt_update_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_opts opts); \ + \ +int bpt_linear_search_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_key(T) key, \ + t_bpt_ndi(T) *ndi, \ + t_bpt_opts opts); \ + \ +int bpt_dichotomic_search_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_key(T) key, \ + t_bpt_ndi(T) *ndi, \ + t_bpt_opts opts); \ + \ +int bpt_search_entry_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_key(T) key, \ + t_bpt_ndi(T) *ndi, \ + t_bpt_opts opts); \ + \ +int bpt_search_leaf_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_node(T) *leaf, \ + t_bpt_key(T) key); \ + \ +int bpt_search_##T(t_bpt(T) *bpt, \ + t_bpt_key(T) key, \ + t_bpt_entry(T) *entry); \ + \ +int bpt_collide_next_##T(t_bpt(T) *bpt, \ + t_bpt_key(T) key, \ + t_bpt_entry(T) *entry); \ + \ +int bpt_collide_search_##T(t_bpt(T) *bpt, \ + t_bpt_key(T) key, \ + t_bpt_value(T) value, \ + t_bpt_entry(T) *entry); \ + \ +int bpt_check_collide_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node1, \ + t_bpt_key(T) key, \ + t_bpt_value(T) value); \ + \ +t_bpt_ndi(T) bpt_node_size_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node); \ + \ +void bpt_simplify_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_unused(T) *unused); \ + \ +int bpt_balancein_1_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node1, \ + t_bpt_imm(T) *node2, \ + t_bpt_cbctx(T) *cbctx, \ + t_bpt_entry(T) prev, \ + t_bpt_unused(T) *unused); \ + \ +int bpt_balancein_2_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node1, \ + t_bpt_imm(T) *node2, \ + t_bpt_cbctx(T) *cbctx, \ + t_bpt_entry(T) prev, \ + t_bpt_unused(T) *unused); \ + \ +int bpt_balancein_3_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node2, \ + t_bpt_imm(T) *node1, \ + t_bpt_cbctx(T) *cbctx, \ + t_bpt_entry(T) prev, \ + t_bpt_unused(T) *unused); \ + \ +int bpt_balancein_4_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node2, \ + t_bpt_imm(T) *node1, \ + t_bpt_cbctx(T) *cbctx, \ + t_bpt_entry(T) prev, \ + t_bpt_unused(T) *unused); \ + \ +int bpt_delete_##T(t_bpt(T) *bpt, \ + t_bpt_entry(T) entry, \ + t_bpt_cbctx(T) *cbctx, \ + t_bpt_unused(T) *unused); \ + \ +int bpt_remove_##T(t_bpt(T) *bpt, \ + t_bpt_key(T) key, \ + t_bpt_unused(T) *unused); \ + \ +int bpt_collide_remove_##T(t_bpt(T) *bpt, \ + t_bpt_entry(T) entry, \ + t_bpt_unused(T) *unused); \ + \ +int bpt_modify_##T(t_bpt(T) *bpt, \ + t_bpt_key(T) key, \ + t_bpt_lfentry(T) *lfentry, \ + t_bpt_unused(T) *unused); \ + \ +int bpt_collide_modify_##T(t_bpt(T) *bpt, \ + t_bpt_entry(T) entry, \ + t_bpt_lfentry(T) *lfentry, \ + t_bpt_unused(T) *unused); \ + \ +void bpt_insert_head_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node1, \ + t_bpt_imm(T) *node2); \ + \ +void bpt_insert_tail_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node1, \ + t_bpt_imm(T) *node2); \ + \ +void bpt_shift_sort_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node); \ + \ +void bpt_insert_sort_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_key(T) key, \ + t_bpt_ndi(T) *ndi, \ + t_bpt_opts opts); \ + \ +void bpt_new_root_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node1, \ + t_bpt_imm(T) *node2, \ + t_bpt_unused(T) *unused); \ + \ +int bpt_balanceout_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node1, \ + t_bpt_imm(T) *node2, \ + void *entry, \ + t_bpt_cbctx(T) *cbctx); \ + \ +int bpt_split_node_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node1, \ + void *entry, \ + t_bpt_cbctx(T) *cbctx, \ + t_bpt_unused(T) *unused); \ + \ +int bpt_insert_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + void *entry, \ + t_bpt_cbctx(T) *cbctx, \ + t_bpt_unused(T) *unused); \ + \ +int bpt_add_##T(t_bpt(T) *bpt, \ + t_bpt_lfentry(T) *lfentry, \ + t_bpt_unused(T) *unused); \ + \ +int bpt_init_##T(t_bpt(T) *bpt, \ + t_bpt_nodesz(T) nodesz, \ + t_bpt_addr(T) uaddr, \ + t_bpt_key(T) ukey, \ + t_bpt_value(T) uval, \ + t_bpt_flags flags, \ + t_bpt_load_fn(T) load, \ + t_bpt_unload_fn(T) unload, \ + t_bpt_addrcmp_fn(T) addrcmp, \ + t_bpt_keycmp_fn(T) keycmp, \ + t_bpt_valcmp_fn(T) valcmp, \ + t_bpt_cb_fn(T) callback, \ + void *data, \ + t_bpt_unused(T) *unused); \ + \ +int bpt_clean_node_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_unused(T) *unused); \ + \ +int bpt_clean_##T(t_bpt(T) *bpt, \ + t_bpt_unused(T) *unused); + +#define bpt_make_functions(T, _key_, _value_) \ + \ +int bpt_foreach_##T(t_bpt(T) *bpt, \ + t_bpt_entry(T) *entry) \ +{ \ + if (entry->node == bpt->uaddr) \ + { \ + t_bpt_imm(T) node; \ + \ + BPT_LOAD(bpt, &node, BPT_GET_ROOT(bpt)); \ + \ + if (bpt_list(T, bpt, &node, entry, BPT_OPT_HEAD) != 0) \ + { \ + BPT_UNLOAD(bpt, &node); \ + return (-1); \ + } \ + \ + BPT_UNLOAD(bpt, &node); \ + \ + return (0); \ + } \ + \ + if (bpt_next_entry(T, bpt, *entry, entry, BPT_OPT_TREE) != 0) \ + return (-1); \ + \ + return (0); \ +} \ + \ +/* \ + * this function finds the head/tail entry in the leaf linked list \ + */ \ + \ +int bpt_list_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_entry(T) *entry, \ + t_bpt_opts opts) \ +{ \ + t_bpt_imm(T) child; \ + t_bpt_ndi(T) i; \ + \ + if (BPT_GET_HEAD(T, node, type) == BPT_TYPE_LEAF) \ + { \ + entry->node = node->addr; \ + if (opts & BPT_OPT_HEAD) \ + { \ + if (bpt_first_entry(T, bpt, node, &entry->ndi) != 0) \ + return (-1); \ + } \ + else if (opts & BPT_OPT_TAIL) \ + { \ + if (bpt_last_entry(T, bpt, node, &entry->ndi) != 0) \ + return (-1); \ + } \ + else \ + return (-1); \ + \ + return (0); \ + } \ + \ + if (opts & BPT_OPT_HEAD) \ + { \ + if (bpt_first_entry(T, bpt, node, &i) != 0) \ + return (-1); \ + } \ + else if (opts & BPT_OPT_TAIL) \ + { \ + if (bpt_last_entry(T, bpt, node, &i) != 0) \ + return (-1); \ + } \ + else \ + return (-1); \ + \ + BPT_LOAD(bpt, &child, BPT_GET_INENTRY(T, node, i, _value_)); \ + \ + if (bpt_list(T, bpt, &child, entry, opts) != 0) \ + { \ + BPT_UNLOAD(bpt, &child); \ + return (-1); \ + } \ + \ + BPT_UNLOAD(bpt, &child); \ + \ + return (0); \ +} \ + \ +/* \ + * this function checks whether the unused array was correctly built \ + */ \ + \ +int bpt_check_unused_##T(t_bpt(T) *bpt, \ + t_bpt_unused(T) *unused, \ + t_bpt_opts opts) \ +{ \ + if (opts == BPT_OPT_INIT) \ + { \ + if (unused->index >= (BPT_INIT_ALLOC() - 1)) \ + return (0); \ + } \ + else if (opts == BPT_OPT_ADD) \ + { \ + if (unused->index >= (BPT_ADD_ALLOC(bpt) - 1)) \ + return (0); \ + } \ + else if (opts == BPT_OPT_MODIFY) \ + { \ + if (unused->index >= (BPT_MODIFY_ALLOC(bpt) - 1)) \ + return (0); \ + } \ + else if (opts == BPT_OPT_REMOVE) \ + { \ + if (unused->index >= (BPT_REMOVE_ALLOC(bpt) - 1)) \ + return (0); \ + } \ + else if (opts == BPT_OPT_CLEAN) \ + { \ + if (unused->index >= (BPT_CLEAN_ALLOC(bpt) - 1)) \ + return (0); \ + } \ + \ + return (-1); \ +} \ + \ +/* \ + * this functions returns the first entry of the node \ + */ \ + \ +int bpt_first_entry_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_ndi(T) *first) \ +{ \ + t_bpt_ndi(T) nkeys = BPT_NKEYS(T, bpt, node); \ + t_bpt_ndi(T) i; \ + \ + for (i = 0; i < nkeys; i++) \ + if (BPT_KEYCMP(bpt, BPT_GET_ENTRY(T, node, i, _key_), bpt->ukey) != 0) \ + { \ + *first = i; \ + return (0); \ + } \ + \ + return (-1); \ +} \ + \ +/* \ + * this function returns the previous entry \ + */ \ + \ +int bpt_prev_entry_##T(t_bpt(T) *bpt, \ + t_bpt_entry(T) current, \ + t_bpt_entry(T) *previous, \ + t_bpt_opts opts) \ +{ \ + if (opts & BPT_OPT_NODE) \ + { \ + t_bpt_ndi(T) nkeys; \ + t_bpt_imm(T) node; \ + t_bpt_ndi(T) i; \ + \ + if ((current.ndi - 1) < 0) \ + return (-1); \ + \ + BPT_LOAD(bpt, &node, current.node); \ + nkeys = BPT_NKEYS(T, bpt, &node); \ + \ + if (current.ndi >= nkeys) \ + { \ + BPT_UNLOAD(bpt, &node); \ + return (-1); \ + } \ + \ + for (i = current.ndi - 1; i >= 0; i--) \ + if (BPT_KEYCMP(bpt, BPT_GET_ENTRY(T, &node, i, _key_), \ + bpt->ukey) != 0) \ + { \ + previous->node = node.addr; \ + previous->ndi = i; \ + \ + BPT_UNLOAD(bpt, &node); \ + \ + return (0); \ + } \ + \ + BPT_UNLOAD(bpt, &node); \ + } \ + \ + if (opts & BPT_OPT_TREE) \ + { \ + t_bpt_imm(T) node; \ + t_bpt_imm(T) prev; \ + \ + if (bpt_prev_entry(T, bpt, current, previous, BPT_OPT_NODE) == 0) \ + return (0); \ + \ + do \ + { \ + BPT_LOAD(bpt, &node, current.node); \ + \ + if (BPT_GET_HEAD(T, &node, prv) == bpt->uaddr) \ + { \ + BPT_UNLOAD(bpt, &node); \ + return (-1); \ + } \ + \ + BPT_LOAD(bpt, &prev, BPT_GET_HEAD(T, &node, prv)); \ + BPT_UNLOAD(bpt, &node); \ + \ + previous->node = prev.addr; \ + if (bpt_last_entry(T, bpt, &prev, &previous->ndi) == 0) \ + { \ + BPT_UNLOAD(bpt, &prev); \ + return (0); \ + } \ + \ + current.node = prev.addr; \ + \ + BPT_UNLOAD(bpt, &prev); \ + } while (1); \ + \ + return (0); \ + } \ + \ + return (-1); \ +} \ + \ +/* \ + * this function returns the next entry \ + */ \ + \ +int bpt_next_entry_##T(t_bpt(T) *bpt, \ + t_bpt_entry(T) current, \ + t_bpt_entry(T) *next, \ + t_bpt_opts opts) \ +{ \ + if (opts & BPT_OPT_NODE) \ + { \ + t_bpt_ndi(T) nkeys; \ + t_bpt_imm(T) node; \ + t_bpt_ndi(T) i; \ + \ + BPT_LOAD(bpt, &node, current.node); \ + nkeys = BPT_NKEYS(T, bpt, &node); \ + \ + if ((current.ndi >= nkeys) || \ + ((current.ndi + 1) >= nkeys)) \ + { \ + BPT_UNLOAD(bpt, &node); \ + return (-1); \ + } \ + \ + for (i = current.ndi + 1; i < nkeys; i++) \ + if (BPT_KEYCMP(bpt, BPT_GET_ENTRY(T, &node, i, _key_), \ + bpt->ukey) != 0) \ + { \ + next->node = node.addr; \ + next->ndi = i; \ + \ + BPT_UNLOAD(bpt, &node); \ + \ + return (0); \ + } \ + \ + BPT_UNLOAD(bpt, &node); \ + } \ + \ + if (opts & BPT_OPT_TREE) \ + { \ + t_bpt_imm(T) node; \ + t_bpt_imm(T) nxt; \ + \ + if (bpt_next_entry(T, bpt, current, next, BPT_OPT_NODE) == 0) \ + return (0); \ + \ + do \ + { \ + BPT_LOAD(bpt, &node, current.node); \ + \ + if (BPT_GET_HEAD(T, &node, nxt) == bpt->uaddr) \ + { \ + BPT_UNLOAD(bpt, &node); \ + return (-1); \ + } \ + \ + BPT_LOAD(bpt, &nxt, BPT_GET_HEAD(T, &node, nxt)); \ + BPT_UNLOAD(bpt, &node); \ + \ + next->node = nxt.addr; \ + if (bpt_first_entry(T, bpt, &nxt, &next->ndi) == 0) \ + { \ + BPT_UNLOAD(bpt, &nxt); \ + return (0); \ + } \ + \ + current.node = nxt.addr; \ + \ + BPT_UNLOAD(bpt, &nxt); \ + } while (1); \ + \ + return (0); \ + } \ + \ + return (-1); \ +} \ + \ +/* \ + * this function returns the last entry of the node \ + */ \ + \ +int bpt_last_entry_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_ndi(T) *last) \ +{ \ + if ((*last = bpt_node_size(T, bpt, node) - 1) < 0) \ + return (-1); \ + \ + return (0); \ +} \ + \ +/* \ + * this function simply reinitializes entries \ + */ \ + \ +void bpt_reinit_entries_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node) \ +{ \ + t_bpt_ndi(T) nkeys = BPT_NKEYS(T, bpt, node); \ + t_bpt_ndi(T) i; \ + \ + for (i = 0; i < nkeys; i++) \ + { \ + BPT_INIT_ENTRY(T, node, i); \ + BPT_SET_ENTRY(T, node, i, _key_, bpt->ukey); \ + \ + if (BPT_GET_HEAD(T, node, type) == BPT_TYPE_INTERNAL) \ + { \ + BPT_SET_INENTRY(T, node, i, _value_, bpt->uaddr); \ + } \ + else \ + { \ + BPT_SET_LFENTRY(T, node, i, _value_, bpt->uval); \ + } \ + } \ +} \ + \ +/* \ + * this function creates and initializes a node \ + */ \ + \ +void bpt_make_node_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_type type) \ +{ \ + t_bpt_ndi(T) nkeys; \ + t_bpt_ndi(T) i; \ + \ + nkeys = type == BPT_TYPE_INTERNAL ? bpt->nikeys : bpt->nlkeys; \ + \ + BPT_SET_HEAD(T, node, type, type); \ + BPT_SET_HEAD(T, node, parent, bpt->uaddr); \ + BPT_SET_HEAD(T, node, prv, bpt->uaddr); \ + BPT_SET_HEAD(T, node, nxt, bpt->uaddr); \ + \ + for (i = 0; i < nkeys; i++) \ + { \ + BPT_INIT_ENTRY(T, node, i); \ + BPT_SET_ENTRY(T, node, i, _key_, bpt->ukey); \ + \ + if (BPT_GET_HEAD(T, node, type) == BPT_TYPE_INTERNAL) \ + { \ + BPT_SET_INENTRY(T, node, i, _value_, bpt->uaddr); \ + } \ + else \ + { \ + BPT_SET_LFENTRY(T, node, i, _value_, bpt->uval); \ + } \ + } \ +} \ + \ +/* \ + * this function looks for the highest key present in a node \ + */ \ + \ +void bpt_key_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_key(T) *key) \ +{ \ + t_bpt_ndi(T) ndi; \ + \ + bpt_last_entry(T, bpt, node, &ndi); \ + \ + *key = BPT_GET_ENTRY(T, node, ndi, _key_); \ +} \ + \ +/* \ + * this function tries to find the more appropriate entry that matchs \ + * with the parameter value. finally the function returns the \ + * node index. \ + * \ + * the value field is used because two entries cannot have the same \ + * value field but can have the same key: collision. \ + * \ + * so the value field is used to retrieve an entry among collisions. \ + */ \ + \ +int bpt_ndi_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_addr(T) value, \ + t_bpt_ndi(T) *ndi) \ +{ \ + t_bpt_ndi(T) nkeys = BPT_NKEYS(T, bpt, node); \ + t_bpt_ndi(T) i; \ + \ + for (i = 0; (i < nkeys) && \ + (BPT_KEYCMP(bpt, BPT_GET_INENTRY(T, node, i, _key_), \ + bpt->ukey) != 0); \ + i++) \ + if (BPT_ADDRCMP(bpt, BPT_GET_INENTRY(T, node, i, _value_), value) == 0) \ + { \ + *ndi = i; \ + return (0); \ + } \ + \ + return (-1); \ +} \ + \ +/* \ + * this function updates the key field \ + */ \ + \ +int bpt_update_node_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_node(T) addr, \ + t_bpt_key(T) key, \ + t_bpt_opts opts) \ +{ \ + t_bpt_key(T) oldk = 0; \ + t_bpt_key(T) newk = 0; \ + t_bpt_imm(T) parent; \ + t_bpt_ndi(T) ndi; \ + \ + if (bpt_ndi(T, bpt, node, addr, &ndi) != 0) \ + { \ + bpt_debug(stderr, "[bptd] panic: this case should never happen\n"); \ + return (-1); \ + } \ + \ + if (opts & BPT_OPT_KEY) \ + { \ + bpt_key(T, bpt, node, &oldk); \ + BPT_SET_ENTRY(T, node, ndi, _key_, key); \ + } \ + \ + if (BPT_ADDRCMP(bpt, BPT_GET_HEAD(T, node, parent), bpt->uaddr) == 0) \ + return (0); \ + \ + bpt_key(T, bpt, node, &newk); \ + \ + if (!(opts & BPT_OPT_KEY)) \ + oldk = newk; \ + \ + if (newk == oldk) \ + return (0); \ + \ + BPT_LOAD(bpt, &parent, BPT_GET_HEAD(T, node, parent)); \ + \ + if (bpt_update_node(T, bpt, &parent, node->addr, newk, opts) != 0) \ + { \ + BPT_UNLOAD(bpt, &parent); \ + return (-1); \ + } \ + \ + BPT_UNLOAD(bpt, &parent); \ + \ + return (0); \ +} \ + \ +/* \ + * this function updates the parent field of each children \ + */ \ + \ +void bpt_update_parent_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node) \ +{ \ + t_bpt_ndi(T) nkeys = BPT_NKEYS(T, bpt, node); \ + t_bpt_ndi(T) i; \ + \ + for (i = 0; (i < nkeys) && \ + (BPT_KEYCMP(bpt, BPT_GET_INENTRY(T, node, i, _key_), \ + bpt->ukey) != 0); \ + i++) \ + { \ + t_bpt_imm(T) child; \ + \ + BPT_LOAD(bpt, &child, BPT_GET_INENTRY(T, node, i, _value_)); \ + BPT_SET_HEAD(T, &child, parent, node->addr); \ + BPT_UNLOAD(bpt, &child); \ + } \ +} \ + \ +/* \ + * this function recursively updates the parent node \ + */ \ + \ +int bpt_update_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_opts opts) \ +{ \ + t_bpt_imm(T) parent; \ + t_bpt_key(T) key; \ + \ + if (opts & BPT_OPT_PARENT) \ + { \ + bpt_update_parent(T, bpt, node); \ + opts &= ~BPT_OPT_PARENT; \ + } \ + \ + if (BPT_GET_HEAD(T, node, parent) == bpt->uaddr) \ + return (0); \ + \ + if (opts == BPT_OPT_ZERO) \ + return (0); \ + \ + bpt_key(T, bpt, node, &key); \ + \ + BPT_LOAD(bpt, &parent, BPT_GET_HEAD(T, node, parent)); \ + \ + if (bpt_update_node(T, bpt, &parent, node->addr, key, opts) != 0) \ + { \ + BPT_UNLOAD(bpt, &parent); \ + return (-1); \ + } \ + \ + BPT_UNLOAD(bpt, &parent); \ + \ + return (0); \ +} \ + \ +/* \ + * this function linearly searchs a key \ + */ \ + \ +int bpt_linear_search_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_key(T) key, \ + t_bpt_ndi(T) *ndi, \ + t_bpt_opts opts) \ +{ \ + t_bpt_ndi(T) nkeys = BPT_NKEYS(T, bpt, node); \ + t_bpt_ndi(T) i; \ + \ + for (i = 0; (i < nkeys) && \ + (BPT_KEYCMP(bpt, BPT_GET_ENTRY(T, node, i, _key_), bpt->ukey) != 0); \ + i++) \ + { \ + if (opts & BPT_OPT_PARTIAL) \ + if ((BPT_KEYCMP(bpt, key, BPT_GET_ENTRY(T, node, i, _key_)) <= 0) || \ + ((i + 1) >= nkeys) || \ + (BPT_KEYCMP(bpt, BPT_GET_ENTRY(T, node, i + 1, _key_), \ + bpt->ukey) == 0)) \ + { \ + *ndi = i; \ + return (0); \ + } \ + \ + if (opts & BPT_OPT_PERFECT) \ + if (BPT_KEYCMP(bpt, BPT_GET_ENTRY(T, node, i, _key_), key) == 0) \ + { \ + *ndi = i; \ + return (0); \ + } \ + } \ + \ + return (-1); \ +} \ + \ +/* \ + * this function looks for a key using a dichotomic search \ + */ \ + \ +int bpt_dichotomic_search_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_key(T) key, \ + t_bpt_ndi(T) *ndi, \ + t_bpt_opts opts) \ +{ \ + t_bpt_ndi(T) j = BPT_NKEYS(T, bpt, node) - 1; \ + t_bpt_ndi(T) i = 0; \ + \ + if (BPT_KEYCMP(bpt, BPT_GET_ENTRY(T, node, 0, _key_), bpt->ukey) == 0) \ + { \ + *ndi = 0; \ + return (opts & BPT_OPT_PARTIAL ? 0 : -1); \ + } \ + \ + while (i <= j) \ + { \ + t_bpt_ndi(T) k = (i + j) / 2; \ + \ + if (BPT_KEYCMP(bpt, BPT_GET_ENTRY(T, node, k, _key_), bpt->ukey) == 0) \ + { \ + j = k - 1; \ + continue; \ + } \ + \ + if (opts & BPT_OPT_PERFECT) \ + if (BPT_KEYCMP(bpt, BPT_GET_ENTRY(T, node, k, _key_), key) == 0) \ + { \ + *ndi = k; \ + return (0); \ + } \ + \ + if (opts & BPT_OPT_PARTIAL) \ + if ((BPT_KEYCMP(bpt, key, BPT_GET_ENTRY(T, node, k, _key_)) <= 0) && \ + ((k == 0) || \ + (BPT_KEYCMP(bpt, key, \ + BPT_GET_ENTRY(T, node, k - 1, _key_)) > 0))) \ + { \ + *ndi = k; \ + return (0); \ + } \ + \ + if (BPT_KEYCMP(bpt, key, BPT_GET_ENTRY(T, node, k, _key_)) < 0) \ + j = k - 1; \ + else \ + i = k + 1; \ + } \ + \ + if (opts & BPT_OPT_PARTIAL) \ + { \ + if (bpt_last_entry(T, bpt, node, ndi) != 0) \ + return (-1); \ + \ + return (0); \ + } \ + \ + return (-1); \ +} \ + \ +/* \ + * this function is used to find an entry in a node. \ + * this function takes the choice of using either a linear or dichotomic \ + * search depending of the flags. \ + */ \ + \ +int bpt_search_entry_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_key(T) key, \ + t_bpt_ndi(T) *ndi, \ + t_bpt_opts opts) \ +{ \ + if (bpt->flags & BPT_FLAG_COLLIDE) \ + return (bpt_linear_search(T, bpt, node, key, ndi, opts)); \ + else \ + return (bpt_dichotomic_search(T, bpt, node, key, ndi, opts)); \ +} \ + \ +/* \ + * this function looks for the leaf entry that theorically contains \ + * the key \ + */ \ + \ +int bpt_search_leaf_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_node(T) *leaf, \ + t_bpt_key(T) key) \ +{ \ + t_bpt_imm(T) child; \ + t_bpt_ndi(T) ndi; \ + \ + if (BPT_GET_HEAD(T, node, type) == BPT_TYPE_LEAF) \ + { \ + *leaf = node->addr; \ + return (0); \ + } \ + \ + if (bpt_search_entry(T, bpt, node, key, &ndi, BPT_OPT_PARTIAL) != 0) \ + return (-1); \ + \ + BPT_LOAD(bpt, &child, BPT_GET_INENTRY(T, node, ndi, _value_)); \ + \ + if (bpt_search_leaf(T, bpt, &child, leaf, key) != 0) \ + { \ + BPT_UNLOAD(bpt, &child); \ + return (-1); \ + } \ + \ + BPT_UNLOAD(bpt, &child); \ + \ + return (0); \ +} \ + \ +/* \ + * this function looks for a key and returns the address of the node and \ + * the index of the entry containing this key. \ + * \ + * be careful of not using this function with collisions because \ + * the bpt_collide_search() function exists. \ + */ \ + \ +int bpt_search_##T(t_bpt(T) *bpt, \ + t_bpt_key(T) key, \ + t_bpt_entry(T) *entry) \ +{ \ + t_bpt_imm(T) leaf; \ + t_bpt_imm(T) root; \ + \ + BPT_LOAD(bpt, &root, BPT_GET_ROOT(bpt)); \ + \ + if (bpt_search_leaf(T, bpt, &root, &entry->node, key) != 0) \ + { \ + bpt_debug(stderr, "[bptd] no leaf present in the tree\n"); \ + BPT_UNLOAD(bpt, &root); \ + return (-1); \ + } \ + \ + BPT_UNLOAD(bpt, &root); \ + BPT_LOAD(bpt, &leaf, entry->node); \ + \ + if (bpt_search_entry(T, bpt, &leaf, key, &entry->ndi, \ + BPT_OPT_PERFECT) != 0) \ + { \ + bpt_debug(stderr, "[bptd] cannot find the entry\n"); \ + BPT_UNLOAD(bpt, &leaf); \ + return (-1); \ + } \ + \ + BPT_UNLOAD(bpt, &leaf); \ + \ + return (0); \ +} \ + \ +/* \ + * this function tries to find the next entry that collide with \ + * the current one specified by the entry variable. \ + * \ + * first try to find an entry with the same key in the current node. if no \ + * one exists, look for in the next node because leaves are doubly linked. \ + * \ + * the function returns -1 if there is no other entry in collision \ + */ \ + \ +int bpt_collide_next_##T(t_bpt(T) *bpt, \ + t_bpt_key(T) key, \ + t_bpt_entry(T) *entry) \ +{ \ + t_bpt_imm(T) node; \ + \ + if (bpt_next_entry(T, bpt, *entry, entry, BPT_OPT_TREE) != 0) \ + return (-1); \ + \ + BPT_LOAD(bpt, &node, entry->node); \ + \ + if (BPT_KEYCMP(bpt, BPT_GET_ENTRY(T, &node, entry->ndi, _key_), key) != 0) \ + { \ + BPT_UNLOAD(bpt, &node); \ + return (-1); \ + } \ + \ + BPT_UNLOAD(bpt, &node); \ + \ + return (0); \ +} \ + \ +/* \ + * this function looks for a leaf entry in collision \ + */ \ + \ +int bpt_collide_search_##T(t_bpt(T) *bpt, \ + t_bpt_key(T) key, \ + t_bpt_value(T) value, \ + t_bpt_entry(T) *entry) \ +{ \ + t_bpt_imm(T) node; \ + \ + if (bpt_search(T, bpt, key, entry) != 0) \ + { \ + bpt_debug(stderr, "[bptd] cannot find the entry in collision\n"); \ + return (-1); \ + } \ + \ + do \ + { \ + BPT_LOAD(bpt, &node, entry->node); \ + \ + if ((BPT_KEYCMP(bpt, BPT_GET_LFENTRY(T, &node, entry->ndi, _key_), \ + key) == 0) && \ + (BPT_VALCMP(bpt, BPT_GET_LFENTRY(T, &node, entry->ndi, _value_), \ + value) == 0)) \ + { \ + BPT_UNLOAD(bpt, &node); \ + return (0); \ + } \ + \ + BPT_UNLOAD(bpt, &node); \ + } while (bpt_collide_next(T, bpt, key, entry) == 0); \ + \ + return (-1); \ +} \ + \ +/* \ + * this function checks whether a full collision (key:value) exists in a leaf \ + */ \ + \ +int bpt_check_collide_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node1, \ + t_bpt_key(T) key, \ + t_bpt_value(T) value) \ +{ \ + t_bpt_imm(T) node2; \ + t_bpt_entry(T) entry; \ + \ + entry.node = node1->addr; \ + if (bpt_search_entry(T, bpt, node1, key, &entry.ndi, BPT_OPT_PERFECT) != 0) \ + return (0); \ + \ + do \ + { \ + BPT_LOAD(bpt, &node2, entry.node); \ + \ + if ((BPT_KEYCMP(bpt, BPT_GET_LFENTRY(T, &node2, entry.ndi, _key_), \ + key) == 0) && \ + (BPT_VALCMP(bpt, BPT_GET_LFENTRY(T, &node2, entry.ndi, _value_), \ + value) == 0)) \ + { \ + BPT_UNLOAD(bpt, &node2); \ + return (-1); \ + } \ + \ + BPT_UNLOAD(bpt, &node2); \ + } while (bpt_collide_next(T, bpt, key, &entry) == 0); \ + \ + return (0); \ +} \ + \ +/* \ + * this function returns the number of used entries in the node \ + * \ + * this function can only be used if the node is sorted and correctly \ + * built \ + */ \ + \ +t_bpt_ndi(T) bpt_node_size_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node) \ +{ \ + t_bpt_ndi(T) nkeys = BPT_NKEYS(T, bpt, node); \ + t_bpt_ndi(T) j = nkeys - 1; \ + t_bpt_ndi(T) i = 0; \ + \ + if (BPT_KEYCMP(bpt, BPT_GET_ENTRY(T, node, 0, _key_), bpt->ukey) == 0) \ + return (0); \ + \ + while (i <= j) \ + { \ + t_bpt_ndi(T) k = (i + j) / 2; \ + \ + if ((BPT_KEYCMP(bpt, BPT_GET_ENTRY(T, node, k, _key_), \ + bpt->ukey) != 0) && \ + (((k + 1) > j) || \ + (BPT_KEYCMP(bpt, BPT_GET_ENTRY(T, node, k + 1, _key_), \ + bpt->ukey) == 0))) \ + return (k + 1); \ + \ + if (BPT_KEYCMP(bpt, BPT_GET_ENTRY(T, node, k, _key_), bpt->ukey) == 0) \ + j = k - 1; \ + else \ + i = k + 1; \ + } \ + \ + return (0); \ +} \ + \ +/* \ + * this function tries to simplify the tree. indeed if the current node \ + * has no neighbour, the tree can be curtail and the node becomes the \ + * root one. \ + */ \ + \ +void bpt_simplify_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node1, \ + t_bpt_unused(T) *unused) \ +{ \ + t_bpt_node(T) parent; \ + t_bpt_imm(T) node2; \ + \ + if (BPT_ADDRCMP(bpt, BPT_GET_HEAD(T, node1, parent), bpt->uaddr) == 0) \ + return ; \ + \ + if ((BPT_ADDRCMP(bpt, BPT_GET_HEAD(T, node1, prv), bpt->uaddr) != 0) || \ + (BPT_ADDRCMP(bpt, BPT_GET_HEAD(T, node1, nxt), bpt->uaddr) != 0)) \ + return ; \ + \ + BPT_LOAD(bpt, &node2, node1->addr); \ + \ + while (BPT_ADDRCMP(bpt, BPT_GET_HEAD(T, &node2, parent), bpt->uaddr) != 0) \ + { \ + parent = BPT_GET_HEAD(T, &node2, parent); \ + \ + BPT_UNLOAD(bpt, &node2); \ + \ + BPT_LOAD(bpt, &node2, parent); \ + \ + BPT_RELEASE(bpt, unused, node2.addr); \ + } \ + \ + BPT_UNLOAD(bpt, &node2); \ + \ + BPT_SET_HEAD(T, node1, parent, bpt->uaddr); \ + BPT_SET_ROOT(bpt, node1->addr); \ +} \ + \ +/* \ + * this function just moves node1's entries in node2 and then \ + * eliminate the node1 'cause it's now unused. \ + */ \ + \ +int bpt_balancein_1_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node1, \ + t_bpt_imm(T) *node2, \ + t_bpt_cbctx(T) *cbctx, \ + t_bpt_entry(T) prev, \ + t_bpt_unused(T) *unused) \ +{ \ + t_bpt_imm(T) parent; \ + t_bpt_entry(T) entry; \ + \ + bpt_insert_head(T, bpt, node1, node2); \ + \ + BPT_SET_HEAD(T, node2, prv, BPT_GET_HEAD(T, node1, prv)); \ + if (BPT_ADDRCMP(bpt, BPT_GET_HEAD(T, node2, prv), bpt->uaddr) != 0) \ + { \ + t_bpt_imm(T) node3; \ + \ + BPT_LOAD(bpt, &node3, BPT_GET_HEAD(T, node2, prv)); \ + BPT_SET_HEAD(T, &node3, nxt, node2->addr); \ + BPT_UNLOAD(bpt, &node3); \ + } \ + \ + if (BPT_GET_HEAD(T, node2, type) == BPT_TYPE_INTERNAL) \ + if (bpt_update(T, bpt, node2, BPT_OPT_PARENT) != 0) \ + return (-1); \ + \ + BPT_LOAD(bpt, &parent, BPT_GET_HEAD(T, node1, parent)); \ + \ + entry.node = parent.addr; \ + if (bpt_ndi(T, bpt, &parent, node1->addr, &entry.ndi) != 0) \ + { \ + BPT_UNLOAD(bpt, &parent); \ + return (-1); \ + } \ + \ + BPT_UNLOAD(bpt, &parent); \ + \ + if (bpt_delete(T, bpt, entry, cbctx, unused) != 0) \ + return (-1); \ + \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node2, type), cbctx, \ + cb, BPT_CB_MIGRATE); \ + if (BPT_ADDRCMP(bpt, prev.node, node1->addr) == 0) \ + { \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node2, type), cbctx, \ + previous.node, node2->addr); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node2, type), cbctx, \ + previous.ndi, prev.ndi); \ + } \ + else \ + { \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node2, type), cbctx, \ + previous.node, prev.node); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node2, type), cbctx, \ + previous.ndi, prev.ndi); \ + } \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node2, type), cbctx, \ + node, node2->addr); \ + \ + BPT_RELEASE(bpt, unused, node1->addr); \ + \ + return (0); \ +} \ + \ +/* \ + * this function just takes an entry in the next node and inserts it \ + * in the current node to not have to move the entries \ + */ \ + \ +int bpt_balancein_2_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node1, \ + t_bpt_imm(T) *node2, \ + t_bpt_cbctx(T) *cbctx, \ + t_bpt_entry(T) prev, \ + t_bpt_unused(T) *unused) \ +{ \ + t_bpt_ndi(T) ndi1; \ + t_bpt_ndi(T) ndi2; \ + \ + bpt_insert_sort(T, bpt, node1, BPT_OPT_NOKEY(T), &ndi1, BPT_OPT_TAIL); \ + \ + if (bpt_first_entry(T, bpt, node2, &ndi2) != 0) \ + return (-1); \ + \ + BPT_COPY_ENTRY(T, node2, node1, ndi2, ndi1); \ + \ + BPT_INIT_ENTRY(T, node2, ndi2); \ + BPT_SET_ENTRY(T, node2, ndi2, _key_, bpt->ukey); \ + \ + bpt_shift_sort(T, bpt, node2); \ + \ + if (BPT_GET_HEAD(T, node1, type) == BPT_TYPE_INTERNAL) \ + if (bpt_update(T, bpt, node1, BPT_OPT_PARENT) != 0) \ + return (-1); \ + \ + if (bpt_update(T, bpt, node1, BPT_OPT_KEY) != 0) \ + return (-1); \ + \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node1, type), cbctx, \ + cb, BPT_CB_BALANCE); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node1, type), cbctx, \ + previous.node, prev.node); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node1, type), cbctx, \ + previous.ndi, prev.ndi); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node1, type), cbctx, \ + node1, node1->addr); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node1, type), cbctx, \ + node2, node2->addr); \ + \ + return (0); \ +} \ + \ +/* \ + * this function balances node1's entries in the node2 and marks \ + * node1 as unused \ + */ \ + \ +int bpt_balancein_3_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node2, \ + t_bpt_imm(T) *node1, \ + t_bpt_cbctx(T) *cbctx, \ + t_bpt_entry(T) prev, \ + t_bpt_unused(T) *unused) \ +{ \ + t_bpt_ndi(T) node1sz = bpt_node_size(T, bpt, node1); \ + t_bpt_imm(T) parent; \ + t_bpt_entry(T) entry; \ + \ + bpt_insert_tail(T, bpt, node2, node1); \ + \ + BPT_SET_HEAD(T, node1, nxt, BPT_GET_HEAD(T, node2, nxt)); \ + if (BPT_ADDRCMP(bpt, BPT_GET_HEAD(T, node1, nxt), bpt->uaddr) != 0) \ + { \ + t_bpt_imm(T) node3; \ + \ + BPT_LOAD(bpt, &node3, BPT_GET_HEAD(T, node2, nxt)); \ + BPT_SET_HEAD(T, &node3, prv, node1->addr); \ + BPT_UNLOAD(bpt, &node3); \ + } \ + \ + if (BPT_GET_HEAD(T, node2, type) == BPT_TYPE_INTERNAL) \ + if (bpt_update(T, bpt, node1, BPT_OPT_PARENT) != 0) \ + return (-1); \ + \ + if (bpt_update(T, bpt, node1, BPT_OPT_KEY) != 0) \ + return (-1); \ + \ + BPT_LOAD(bpt, &parent, BPT_GET_HEAD(T, node2, parent)); \ + \ + entry.node = BPT_GET_HEAD(T, node2, parent); \ + if (bpt_ndi(T, bpt, &parent, node2->addr, &entry.ndi) != 0) \ + { \ + BPT_UNLOAD(bpt, &parent); \ + return (-1); \ + } \ + \ + BPT_UNLOAD(bpt, &parent); \ + \ + if (bpt_delete(T, bpt, entry, cbctx, unused) != 0) \ + return (-1); \ + \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node1, type), cbctx, \ + cb, BPT_CB_MIGRATE); \ + if (BPT_ADDRCMP(bpt, prev.node, node2->addr) == 0) \ + { \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node1, type), cbctx, \ + previous.node, node1->addr); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node1, type), cbctx, \ + previous.ndi, node1sz + prev.ndi); \ + } \ + else \ + { \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node1, type), cbctx, \ + previous.node, prev.node); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node1, type), cbctx, \ + previous.ndi, prev.ndi); \ + } \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node1, type), cbctx, \ + node, node1->addr); \ + \ + BPT_RELEASE(bpt, unused, node2->addr); \ + \ + return (0); \ +} \ + \ +/* \ + * this function moves one entry of the node2 in the node1 \ + */ \ + \ +int bpt_balancein_4_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node2, \ + t_bpt_imm(T) *node1, \ + t_bpt_cbctx(T) *cbctx, \ + t_bpt_entry(T) prev, \ + t_bpt_unused(T) *unused) \ +{ \ + t_bpt_ndi(T) ndi1; \ + t_bpt_ndi(T) ndi2; \ + \ + bpt_insert_sort(T, bpt, node2, BPT_OPT_NOKEY(T), &ndi2, BPT_OPT_HEAD); \ + \ + if (bpt_last_entry(T, bpt, node1, &ndi1) != 0) \ + return (-1); \ + \ + BPT_COPY_ENTRY(T, node1, node2, ndi1, ndi2); \ + \ + BPT_INIT_ENTRY(T, node1, ndi1); \ + BPT_SET_ENTRY(T, node1, ndi1, _key_, bpt->ukey); \ + \ + if (BPT_GET_HEAD(T, node2, type) == BPT_TYPE_INTERNAL) \ + if (bpt_update(T, bpt, node2, BPT_OPT_PARENT) != 0) \ + return (-1); \ + \ + if (bpt_update(T, bpt, node1, BPT_OPT_KEY) != 0) \ + return (-1); \ + \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node2, type), cbctx, \ + cb, BPT_CB_BALANCE); \ + if ((BPT_ADDRCMP(bpt, prev.node, node1->addr) == 0) && \ + (prev.ndi == ndi1)) \ + { \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node2, type), cbctx, \ + previous.node, node2->addr); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node2, type), cbctx, \ + previous.ndi, ndi2); \ + } \ + else \ + { \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node2, type), cbctx, \ + previous.node, prev.node); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node2, type), cbctx, \ + previous.ndi, prev.ndi + 1); \ + } \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node2, type), cbctx, \ + node1, node1->addr); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node2, type), cbctx, \ + node2, node2->addr); \ + \ + return (0); \ +} \ + \ +/* \ + * this function deletes an entry in a giving node and rebalances \ + * entries if necessary. \ + * \ + * if a balancing has to be done, the function operates according \ + * to four cases: \ + * \ + * 1) the next node is large enough to receive the entries of the node1 \ + * 2) the next node is not large enough so we equilibrate the two nodes \ + * taking a node2's entry to move it in the node1 \ + * 3) the previous node is large enough to receive the entries of the node1 \ + * 4) the previous node is not large enough so we equilibrate the two nodes \ + * taking a node2's entry to move it in the node1 \ + */ \ + \ +int bpt_delete_##T(t_bpt(T) *bpt, \ + t_bpt_entry(T) entry, \ + t_bpt_cbctx(T) *cbctx, \ + t_bpt_unused(T) *unused) \ +{ \ + t_bpt_imm(T) node1; \ + t_bpt_imm(T) node2; \ + t_bpt_ndi(T) nkeys; \ + t_bpt_entry(T) prev; \ + \ + if (bpt_prev_entry(T, bpt, entry, &prev, BPT_OPT_TREE) != 0) \ + { \ + prev.node = bpt->uaddr; \ + prev.ndi = 0; \ + } \ + \ + BPT_LOAD(bpt, &node1, entry.node); \ + nkeys = BPT_NKEYS(T, bpt, &node1); \ + \ + BPT_INIT_ENTRY(T, &node1, entry.ndi); \ + BPT_SET_ENTRY(T, &node1, entry.ndi, _key_, bpt->ukey); \ + \ + bpt_shift_sort(T, bpt, &node1); \ + \ + if ((BPT_ADDRCMP(bpt, BPT_GET_HEAD(T, &node1, parent), bpt->uaddr) == 0) || \ + (bpt_node_size(T, bpt, &node1) >= BPT_QUOTA(T, bpt, &node1))) \ + { \ + if (bpt_update(T, bpt, &node1, BPT_OPT_KEY) != 0) \ + { \ + bpt_debug(stderr, "[bptd] cannot update the node\n"); \ + BPT_UNLOAD(bpt, &node1); \ + return (-1); \ + } \ + \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, &node1, type), cbctx, \ + cb, BPT_CB_DELETE); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, &node1, type), cbctx, \ + previous.node, prev.node); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, &node1, type), cbctx, \ + previous.ndi, prev.ndi); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, &node1, type), cbctx, \ + node, node1.addr); \ + \ + BPT_UNLOAD(bpt, &node1); \ + \ + return (0); \ + } \ + \ + if (BPT_ADDRCMP(bpt, BPT_GET_HEAD(T, &node1, nxt), bpt->uaddr) != 0) \ + { \ + BPT_LOAD(bpt, &node2, BPT_GET_HEAD(T, &node1, nxt)); \ + \ + if ((bpt_node_size(T, bpt, &node2) + \ + bpt_node_size(T, bpt, &node1)) <= nkeys) \ + { \ + if (bpt_balancein_1(T, bpt, &node1, &node2, cbctx, \ + prev, unused) != 0) \ + { \ + bpt_debug(stderr, "[bptd] cannot balance entries in the " \ + "second node\n"); \ + BPT_UNLOAD(bpt, &node2); \ + BPT_UNLOAD(bpt, &node1); \ + return (-1); \ + } \ + \ + bpt_simplify(T, bpt, &node2, unused); \ + \ + BPT_UNLOAD(bpt, &node2); \ + BPT_UNLOAD(bpt, &node1); \ + return (0); \ + } \ + else \ + { \ + if (bpt_balancein_2(T, bpt, &node1, &node2, cbctx, \ + prev, unused) != 0) \ + { \ + bpt_debug(stderr, "[bptd] cannot balance one entry in " \ + "the node\n"); \ + BPT_UNLOAD(bpt, &node2); \ + BPT_UNLOAD(bpt, &node1); \ + return (-1); \ + } \ + \ + BPT_UNLOAD(bpt, &node2); \ + BPT_UNLOAD(bpt, &node1); \ + return (0); \ + } \ + \ + BPT_UNLOAD(bpt, &node2); \ + } \ + \ + if (BPT_ADDRCMP(bpt, BPT_GET_HEAD(T, &node1, prv), bpt->uaddr) != 0) \ + { \ + BPT_LOAD(bpt, &node2, BPT_GET_HEAD(T, &node1, prv)); \ + \ + if ((bpt_node_size(T, bpt, &node2) + \ + bpt_node_size(T, bpt, &node1)) <= nkeys) \ + { \ + if (bpt_balancein_3(T, bpt, &node1, &node2, cbctx, \ + prev, unused) != 0) \ + { \ + bpt_debug(stderr, "[bptd] cannot balance entries in the " \ + "second node\n"); \ + BPT_UNLOAD(bpt, &node2); \ + BPT_UNLOAD(bpt, &node1); \ + return (-1); \ + } \ + \ + bpt_simplify(T, bpt, &node2, unused); \ + \ + BPT_UNLOAD(bpt, &node2); \ + BPT_UNLOAD(bpt, &node1); \ + return (0); \ + } \ + else \ + { \ + if (bpt_balancein_4(T, bpt, &node1, &node2, cbctx, \ + prev, unused) != 0) \ + { \ + bpt_debug(stderr, "[bptd] cannot balance one entry in " \ + "the node\n"); \ + BPT_UNLOAD(bpt, &node2); \ + BPT_UNLOAD(bpt, &node1); \ + return (-1); \ + } \ + \ + BPT_UNLOAD(bpt, &node2); \ + BPT_UNLOAD(bpt, &node1); \ + return (0); \ + } \ + \ + BPT_UNLOAD(bpt, &node2); \ + } \ + \ + if (bpt_update(T, bpt, &node1, BPT_OPT_KEY) != 0) \ + { \ + bpt_debug(stderr, "[bptd] cannot update the node\n"); \ + BPT_UNLOAD(bpt, &node1); \ + return (-1); \ + } \ + \ + bpt_simplify(T, bpt, &node1, unused); \ + \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, &node1, type), \ + cbctx, cb, BPT_CB_DELETE); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, &node1, type), \ + cbctx, previous.node, prev.node); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, &node1, type), \ + cbctx, previous.ndi, prev.ndi); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, &node1, type), \ + cbctx, node, node1.addr); \ + \ + BPT_UNLOAD(bpt, &node1); \ + \ + return (0); \ +} \ + \ +/* \ + * this function removes a leaf entry \ + * \ + * this function does not take care about collisions assuming there is no \ + * one. like bpt_modify(), if the programmer want to remove a specific \ + * entry that collide with others, he has to use the collide_remove() \ + * function. \ + * \ + * the function also fills the unused array with the addresses of the now \ + * unused blocks. \ + */ \ + \ +int bpt_remove_##T(t_bpt(T) *bpt, \ + t_bpt_key(T) key, \ + t_bpt_unused(T) *unused) \ +{ \ + t_bpt_cbctx(T) cbctx; \ + t_bpt_entry(T) entry; \ + \ + if (bpt_check_unused(T, bpt, unused, BPT_OPT_REMOVE) != 0) \ + { \ + bpt_debug(stderr, "[bptd] bad unused array\n"); \ + return (-1); \ + } \ + \ + if (bpt_search(T, bpt, key, &entry) != 0) \ + { \ + bpt_debug(stderr, "[bptd] cannot find the entry looked for\n"); \ + return (-1); \ + } \ + \ + BPT_INIT_CBCTX(bpt, &cbctx); \ + \ + if (bpt_delete(T, bpt, entry, &cbctx, unused) != 0) \ + { \ + bpt_debug(stderr, "[bptd] cannot delete the leaf entry\n"); \ + return (-1); \ + } \ + \ + BPT_CALLBACK(bpt, &cbctx); \ + \ + return (0); \ +} \ + \ +/* \ + * this function is similar to the bpt_remove() one. \ + * the singular difference is that the function takes an entry descriptor \ + * as parameter. by this, this function is able to remove an entry that \ + * collide with others. \ + */ \ + \ +int bpt_collide_remove_##T(t_bpt(T) *bpt, \ + t_bpt_entry(T) entry, \ + t_bpt_unused(T) *unused) \ +{ \ + t_bpt_cbctx(T) cbctx; \ + \ + if (bpt_check_unused(T, bpt, unused, BPT_OPT_REMOVE) != 0) \ + { \ + bpt_debug(stderr, "[bptd] bad unused array\n"); \ + return (-1); \ + } \ + \ + BPT_INIT_CBCTX(bpt, &cbctx); \ + \ + if (bpt_delete(T, bpt, entry, &cbctx, unused) != 0) \ + { \ + bpt_debug(stderr, "[bptd] cannot delete the leaf entry\n"); \ + return (-1); \ + } \ + \ + BPT_CALLBACK(bpt, &cbctx); \ + \ + return (0); \ +} \ + \ +/* \ + * this function modifies an entry. this entry may be moved due to the \ + * key modification. \ + * \ + * this function supposes that there is no collision. if the tree accepts \ + * collisions, we advice you to not use this function. instead you must \ + * implement your own version or use collide_modify(). \ + * \ + * the unused variable must contain free blocks that the bpt library \ + * can use for splitting. \ + * this restriction may seem abusive, so *ONLY* if the programmer knows \ + * that the new leaf entry has the same key, this function can be called \ + * with a NULL unused variable because it will never be used. \ + * \ + * to know what the unused variable is supposed to contain, take a look \ + * to the bpt_add() function. \ + */ \ + \ +int bpt_modify_##T(t_bpt(T) *bpt, \ + t_bpt_key(T) key, \ + t_bpt_lfentry(T) *lfentry, \ + t_bpt_unused(T) *unused) \ +{ \ + t_bpt_cbctx(T) cbctx; \ + t_bpt_entry(T) entry; \ + t_bpt_imm(T) node; \ + \ + if (bpt_check_unused(T, bpt, unused, BPT_OPT_MODIFY) != 0) \ + { \ + bpt_debug(stderr, "[bptd] bad unused array\n"); \ + return (-1); \ + } \ + \ + if (BPT_KEYCMP(bpt, lfentry->_key_, bpt->ukey) == 0) \ + { \ + bpt_debug(stderr, "[bptd] the leaf entry to update has a " \ + "non-valid key\n"); \ + return (-1); \ + } \ + \ + if (bpt_search(T, bpt, key, &entry) != 0) \ + { \ + bpt_debug(stderr, "[bptd] cannot find the entry\n"); \ + return (-1); \ + } \ + \ + BPT_LOAD(bpt, &node, entry.node); \ + \ + BPT_INIT_CBCTX(bpt, &cbctx); \ + \ + if (BPT_KEYCMP(bpt, lfentry->_key_, \ + BPT_GET_LFENTRY(T, &node, entry.ndi, _key_)) == 0) \ + { \ + BPT_IMPORT_LFENTRY(T, &node, entry.ndi, lfentry); \ + \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, &node, type), &cbctx, \ + cb, BPT_CB_MODIFY); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, &node, type), &cbctx, \ + current.node, node.addr); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, &node, type), &cbctx, \ + current.ndi, entry.ndi); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, &node, type), &cbctx, \ + node, node.addr); \ + \ + BPT_CALLBACK(bpt, &cbctx); \ + \ + BPT_UNLOAD(bpt, &node); \ + \ + return (0); \ + } \ + \ + BPT_UNLOAD(bpt, &node); \ + \ + if (bpt_remove(T, bpt, key, unused) != 0) \ + { \ + bpt_debug(stderr, "[bptd] cannot remove the entry\n"); \ + return (-1); \ + } \ + \ + if (bpt_add(T, bpt, lfentry, unused) != 0) \ + { \ + bpt_debug(stderr, "[bptd] cannot add the entry\n"); \ + return (-1); \ + } \ + \ + return (0); \ +} \ + \ +/* \ + * this function is similar to the bpt_modify() function except the fact \ + * that it takes a entry descriptor to specify a precise entry to modify. \ + * \ + * this function is adapted to the trees that manage collisions. \ + */ \ + \ +int bpt_collide_modify_##T(t_bpt(T) *bpt, \ + t_bpt_entry(T) entry, \ + t_bpt_lfentry(T) *lfentry, \ + t_bpt_unused(T) *unused) \ +{ \ + t_bpt_cbctx(T) cbctx; \ + t_bpt_imm(T) node; \ + \ + if (bpt_check_unused(T, bpt, unused, BPT_OPT_MODIFY) != 0) \ + { \ + bpt_debug(stderr, "[bptd] bad unused array\n"); \ + return (-1); \ + } \ + \ + if (BPT_KEYCMP(bpt, lfentry->_key_, bpt->ukey) == 0) \ + { \ + bpt_debug(stderr, "[bptd] the leaf entry to update has a " \ + "non-valid key\n"); \ + return (-1); \ + } \ + \ + BPT_LOAD(bpt, &node, entry.node); \ + \ + BPT_INIT_CBCTX(bpt, &cbctx); \ + \ + if (BPT_KEYCMP(bpt, lfentry->_key_, \ + BPT_GET_LFENTRY(T, &node, entry.ndi, _key_)) == 0) \ + { \ + BPT_IMPORT_LFENTRY(T, &node, entry.ndi, lfentry); \ + \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, &node, type), &cbctx, \ + cb, BPT_CB_MODIFY); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, &node, type), &cbctx, \ + current.node, node.addr); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, &node, type), &cbctx, \ + current.ndi, entry.ndi); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, &node, type), &cbctx, \ + node, node.addr); \ + \ + BPT_CALLBACK(bpt, &cbctx); \ + \ + BPT_UNLOAD(bpt, &node); \ + \ + return (0); \ + } \ + \ + BPT_UNLOAD(bpt, &node); \ + \ + if (bpt_collide_remove(T, bpt, entry, unused) != 0) \ + { \ + bpt_debug(stderr, "[bptd] cannot remove a collision\n"); \ + return (-1); \ + } \ + \ + if (bpt_add(T, bpt, lfentry, unused) != 0) \ + { \ + bpt_debug(stderr, "[bptd] cannot add an entry\n"); \ + return (-1); \ + } \ + \ + return (0); \ +} \ + \ +/* \ + * this function inserts node1's entries at the head of the node2 \ + */ \ + \ +void bpt_insert_head_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node1, \ + t_bpt_imm(T) *node2) \ +{ \ + t_bpt_ndi(T) node1sz = bpt_node_size(T, bpt, node1); \ + t_bpt_ndi(T) node2sz = bpt_node_size(T, bpt, node2); \ + t_bpt_ndi(T) i; \ + \ + for (i = node2sz - 1; i >= 0; i--) \ + BPT_COPY_ENTRY(T, node2, node2, i, node1sz + i); \ + for (i = 0; i < node1sz; i++) \ + BPT_COPY_ENTRY(T, node1, node2, i, i); \ +} \ + \ +/* \ + * this function inserts node1's entries at the tail of the node2 \ + */ \ + \ +void bpt_insert_tail_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node1, \ + t_bpt_imm(T) *node2) \ +{ \ + t_bpt_ndi(T) node2sz = bpt_node_size(T, bpt, node2); \ + t_bpt_ndi(T) nkeys = BPT_NKEYS(T, bpt, node1); \ + t_bpt_ndi(T) i; \ + \ + for (i = 0; \ + (i < nkeys) && (BPT_KEYCMP(bpt, BPT_GET_ENTRY(T, node1, i, _key_), \ + bpt->ukey) != 0); \ + i++) \ + BPT_COPY_ENTRY(T, node1, node2, i, node2sz + i); \ +} \ + \ +/* \ + * this function shifts entries to place unused entries at the end of \ + * the node \ + */ \ + \ +void bpt_shift_sort_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node) \ +{ \ + t_bpt_ndi(T) nkeys = BPT_NKEYS(T, bpt, node); \ + t_bpt_entry(T) current; \ + \ + for (current.node = node->addr, current.ndi = 0; \ + current.ndi < nkeys; current.ndi++) \ + if (BPT_KEYCMP(bpt, BPT_GET_ENTRY(T, node, current.ndi, _key_), \ + bpt->ukey) == 0) \ + { \ + t_bpt_entry(T) next; \ + \ + if (bpt_next_entry(T, bpt, current, &next, BPT_OPT_NODE) != 0) \ + break; \ + \ + BPT_SWAP_ENTRIES(T, node, current.ndi, next.ndi); \ + } \ +} \ + \ +/* \ + * this function inserts and sorts an entry \ + */ \ + \ +void bpt_insert_sort_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_key(T) key, \ + t_bpt_ndi(T) *ndi, \ + t_bpt_opts opts) \ +{ \ + t_bpt_ndi(T) nodesz = bpt_node_size(T, bpt, node); \ + t_bpt_ndi(T) nkeys = BPT_NKEYS(T, bpt, node); \ + t_bpt_ndi(T) i; \ + \ + if (opts & BPT_OPT_HEAD) \ + { \ + for (*ndi = 0, i = nodesz - 1; i >= 0; i--) \ + BPT_COPY_ENTRY(T, node, node, i, i + 1); \ + } \ + else if (opts & BPT_OPT_TAIL) \ + { \ + *ndi = nodesz; \ + } \ + else \ + { \ + for (*ndi = 0; \ + (*ndi < nkeys) && \ + (BPT_KEYCMP(bpt, BPT_GET_ENTRY(T, node, *ndi, _key_), \ + bpt->ukey) != 0); \ + (*ndi)++) \ + if (BPT_KEYCMP(bpt, BPT_GET_ENTRY(T, node, *ndi, _key_), key) > 0) \ + break; \ + \ + if (nodesz > *ndi) \ + for (i = nodesz - 1; i >= *ndi; i--) \ + BPT_COPY_ENTRY(T, node, node, i, i + 1); \ + } \ + \ + BPT_INIT_ENTRY(T, node, *ndi); \ + BPT_SET_ENTRY(T, node, *ndi, _key_, bpt->ukey); \ +} \ + \ +/* \ + * this function creates a new root node and then inserts two entries \ + * in it for the two children node1 and node2 \ + */ \ + \ +void bpt_new_root_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node1, \ + t_bpt_imm(T) *node2, \ + t_bpt_unused(T) *unused) \ +{ \ + t_bpt_inentry(T) inentry; \ + t_bpt_imm(T) root; \ + t_bpt_addr(T) blk; \ + t_bpt_ndi(T) ndi; \ + \ + BPT_RESERVE(bpt, unused, blk); \ + BPT_LOAD(bpt, &root, blk); \ + \ + bpt_make_node(T, bpt, &root, BPT_TYPE_INTERNAL); \ + \ + memset(&inentry, 0x0, sizeof (t_bpt_inentry(T))); \ + bpt_key(T, bpt, node1, &inentry._key_); \ + inentry._value_ = node1->addr; \ + \ + bpt_insert_sort(T, bpt, &root, inentry._key_, &ndi, BPT_OPT_ZERO); \ + \ + BPT_IMPORT_INENTRY(T, &root, ndi, &inentry); \ + \ + memset(&inentry, 0x0, sizeof (t_bpt_inentry(T))); \ + bpt_key(T, bpt, node2, &inentry._key_); \ + inentry._value_ = node2->addr; \ + \ + bpt_insert_sort(T, bpt, &root, inentry._key_, &ndi, BPT_OPT_ZERO); \ + \ + BPT_IMPORT_INENTRY(T, &root, ndi, &inentry); \ + \ + BPT_SET_ROOT(bpt, root.addr); \ + BPT_SET_HEAD(T, node1, parent, root.addr); \ + BPT_SET_HEAD(T, node2, parent, root.addr); \ + \ + bpt->height++; \ + \ + BPT_UNLOAD(bpt, &root); \ +} \ + \ +/* \ + * this function, used by bpt_split_node() rebalances the \ + * entries of node1 in the two nodes node1 and node2. \ + */ \ + \ +int bpt_balanceout_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node1, \ + t_bpt_imm(T) *node2, \ + void *entry, \ + t_bpt_cbctx(T) *cbctx) \ +{ \ + t_bpt_ndi(T) nkeys = BPT_NKEYS(T, bpt, node1); \ + t_bpt_ndi(T) i; \ + t_bpt_ndi(T) j; \ + \ + BPT_COPY_NODE(bpt, node1->buf, node2->buf); \ + bpt_reinit_entries(T, bpt, node1); \ + \ + for (i = 0, j = 0; i < ((nkeys + 1) / 2); i++) \ + { \ + if ((entry != NULL) && \ + (BPT_KEYCMP(bpt, BPT_GET_THIS_ENTRY(T, BPT_GET_HEAD(T, node1, type),\ + entry, _key_), \ + BPT_GET_ENTRY(T, node2, j, _key_)) < 0)) \ + { \ + BPT_IMPORT_ENTRY(T, node1, i, entry); \ + \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node1, type), cbctx, \ + current.node, node1->addr); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node1, type), cbctx, \ + current.ndi, i); \ + \ + entry = NULL; \ + continue; \ + } \ + \ + BPT_COPY_ENTRY(T, node2, node1, j, i); \ + BPT_INIT_ENTRY(T, node2, j); \ + BPT_SET_ENTRY(T, node2, j, _key_, bpt->ukey); \ + j++; \ + } \ + \ + bpt_shift_sort(T, bpt, node2); \ + \ + if (entry != NULL) \ + { \ + bpt_insert_sort(T, bpt, node2, \ + BPT_GET_THIS_ENTRY(T, BPT_GET_HEAD(T, node1, type), \ + entry, _key_), \ + &i, BPT_OPT_ZERO); \ + BPT_IMPORT_ENTRY(T, node2, i, entry); \ + \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node2, type), cbctx, \ + current.node, node2->addr); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node2, type), cbctx, \ + current.ndi, i); \ + } \ + \ + BPT_SET_HEAD(T, node2, prv, node1->addr); \ + BPT_SET_HEAD(T, node2, nxt, BPT_GET_HEAD(T, node1, nxt)); \ + BPT_SET_HEAD(T, node1, nxt, node2->addr); \ + \ + if (BPT_ADDRCMP(bpt, BPT_GET_HEAD(T, node2, nxt), bpt->uaddr) != 0) \ + { \ + t_bpt_imm(T) node3; \ + \ + BPT_LOAD(bpt, &node3, BPT_GET_HEAD(T, node2, nxt)); \ + BPT_SET_HEAD(T, &node3, prv, node2->addr); \ + BPT_UNLOAD(bpt, &node3); \ + } \ + \ + if (BPT_GET_HEAD(T, node1, type) == BPT_TYPE_INTERNAL) \ + { \ + if (bpt_update(T, bpt, node1, BPT_OPT_PARENT) != 0) \ + return (-1); \ + \ + if (bpt_update(T, bpt, node2, BPT_OPT_PARENT) != 0) \ + return (-1); \ + } \ + \ + return (0); \ +} \ + \ +/* \ + * this function splits a node so creates one new node (using an unused \ + * address) and reorganizes the entries. if splitting the root node, a new \ + * root node has to be created. \ + * the function also inserts the specified entry. \ + * \ + * finally the function fills the entry descriptor that indicates the \ + * location of the inserted entry \ + */ \ + \ +int bpt_split_node_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node1, \ + void *entry, \ + t_bpt_cbctx(T) *cbctx, \ + t_bpt_unused(T) *unused) \ +{ \ + t_bpt_imm(T) node2; \ + t_bpt_addr(T) blk; \ + \ + BPT_RESERVE(bpt, unused, blk); \ + BPT_LOAD(bpt, &node2, blk); \ + \ + bpt_make_node(T, bpt, &node2, BPT_GET_HEAD(T, node1, type)); \ + BPT_SET_HEAD(T, node1, parent, BPT_GET_HEAD(T, node1, parent)); \ + \ + if (bpt_balanceout(T, bpt, node1, &node2, entry, cbctx) != 0) \ + { \ + bpt_debug(stderr, "[bptd] cannot balance entries between two nodes\n"); \ + BPT_UNLOAD(bpt, &node2); \ + BPT_RELEASE(bpt, unused, blk); \ + return (-1); \ + } \ + \ + if (BPT_ADDRCMP(bpt, BPT_GET_HEAD(T, node1, parent), bpt->uaddr) == 0) \ + { \ + bpt_new_root(T, bpt, node1, &node2, unused); \ + } \ + else \ + { \ + t_bpt_inentry(T) inentry; \ + t_bpt_imm(T) parent; \ + \ + if (bpt_update(T, bpt, node1, BPT_OPT_KEY) != 0) \ + { \ + BPT_UNLOAD(bpt, &node2); \ + BPT_RELEASE(bpt, unused, blk); \ + return (-1); \ + } \ + \ + memset(&inentry, 0x0, sizeof (t_bpt_inentry(T))); \ + bpt_key(T, bpt, &node2, &inentry._key_); \ + inentry._value_ = node2.addr; \ + \ + BPT_LOAD(bpt, &parent, BPT_GET_HEAD(T, &node2, parent)); \ + \ + if (bpt_insert(T, bpt, &parent, &inentry, cbctx, unused) != 0) \ + { \ + bpt_debug(stderr, "[bptd] cannot insert the entry in the " \ + "parent node\n"); \ + BPT_UNLOAD(bpt, &parent); \ + BPT_UNLOAD(bpt, &node2); \ + BPT_RELEASE(bpt, unused, blk); \ + return (-1); \ + } \ + \ + BPT_UNLOAD(bpt, &parent); \ + } \ + \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node1, type), cbctx, \ + cb, BPT_CB_SPLIT); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node1, type), cbctx, \ + node1, node1->addr); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node1, type), cbctx, \ + node2, node2.addr); \ + \ + BPT_UNLOAD(bpt, &node2); \ + \ + return (0); \ +} \ + \ +/* \ + * this function inserts the entry in the specified node. \ + * the node may be splitted and the entries rebalanced between the two \ + * nodes. \ + * \ + * the unused variable contains unused blocks, one for each level \ + * of the tree because at each level a node can be splitted and one \ + * for the possible creation of a new root node. \ + */ \ + \ +int bpt_insert_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + void *entry, \ + t_bpt_cbctx(T) *cbctx, \ + t_bpt_unused(T) *unused) \ +{ \ + if (bpt_node_size(T, bpt, node) < BPT_NKEYS(T, bpt, node)) \ + { \ + t_bpt_ndi(T) ndi; \ + \ + bpt_insert_sort(T, bpt, node, \ + BPT_GET_THIS_ENTRY(T, BPT_GET_HEAD(T, node, type), \ + entry, _key_), \ + &ndi, BPT_OPT_ZERO); \ + \ + BPT_IMPORT_ENTRY(T, node, ndi, entry); \ + \ + if (bpt_update(T, bpt, node, BPT_OPT_KEY) != 0) \ + { \ + bpt_debug(stderr, "[bptd] cannot update the tree\n"); \ + return (-1); \ + } \ + \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node, type), cbctx, \ + cb, BPT_CB_INSERT); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node, type), cbctx, \ + current.node, node->addr); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node, type), cbctx, \ + current.ndi, ndi); \ + BPT_SET_CBCTX(bpt, BPT_GET_HEAD(T, node, type), cbctx, \ + node, node->addr); \ + } \ + else \ + { \ + if (bpt_split_node(T, bpt, node, entry, cbctx, unused) != 0) \ + { \ + bpt_debug(stderr, "[bptd] cannot split the node\n"); \ + return (-1); \ + } \ + } \ + \ + return (0); \ +} \ + \ +/* \ + * this function adds the lfentry in a leaf of the tree. \ + * the tree may grow and split some nodes to insert the new entry. \ + * \ + * the variable unused contains N+1 unused block addresses where N is \ + * the tree's height. the '+1' designs the possible creation of a new \ + * root node. \ + * \ + * if the programmer knows that the insertion of the new entry will \ + * only split M node - where M is lower than N - the unused \ + * variable can be built in consequence. \ + */ \ + \ +int bpt_add_##T(t_bpt(T) *bpt, \ + t_bpt_lfentry(T) *lfentry, \ + t_bpt_unused(T) *unused) \ +{ \ + t_bpt_cbctx(T) cbctx; \ + t_bpt_node(T) addr; \ + t_bpt_imm(T) leaf; \ + t_bpt_imm(T) root; \ + \ + if (bpt_check_unused(T, bpt, unused, BPT_OPT_ADD) != 0) \ + { \ + bpt_debug(stderr, "[bptd] bad unused array\n"); \ + return (-1); \ + } \ + \ + if (BPT_KEYCMP(bpt, lfentry->_key_, bpt->ukey) == 0) \ + { \ + bpt_debug(stderr, "[bptd] the leaf entry to insert has a " \ + "non-valid key\n"); \ + return (-1); \ + } \ + \ + BPT_LOAD(bpt, &root, BPT_GET_ROOT(bpt)); \ + \ + if (bpt_search_leaf(T, bpt, &root, &addr, lfentry->_key_) != 0) \ + { \ + bpt_debug(stderr, "[bptd] no leaf present in the tree\n"); \ + BPT_UNLOAD(bpt, &root); \ + return (-1); \ + } \ + \ + BPT_UNLOAD(bpt, &root); \ + BPT_LOAD(bpt, &leaf, addr); \ + \ + if (bpt_check_collide(T, bpt, &leaf, lfentry->_key_, lfentry->_value_) != 0)\ + { \ + bpt_debug(stderr, "[bptd] this entry collide with an other\n"); \ + BPT_UNLOAD(bpt, &leaf); \ + return (-1); \ + } \ + \ + BPT_INIT_CBCTX(bpt, &cbctx); \ + \ + if (bpt_insert(T, bpt, &leaf, lfentry, &cbctx, unused) != 0) \ + { \ + bpt_debug(stderr, "[bptd] cannot insert the leaf entry\n"); \ + BPT_UNLOAD(bpt, &leaf); \ + return (-1); \ + } \ + \ + BPT_CALLBACK(bpt, &cbctx); \ + \ + BPT_UNLOAD(bpt, &leaf); \ + \ + return (0); \ +} \ + \ +/* \ + * this function initializes the core structure and build the tree's \ + * root node \ + */ \ + \ +int bpt_init_##T(t_bpt(T) *bpt, \ + t_bpt_nodesz(T) nodesz, \ + t_bpt_addr(T) uaddr, \ + t_bpt_key(T) ukey, \ + t_bpt_value(T) uval, \ + t_bpt_flags flags, \ + t_bpt_load_fn(T) load, \ + t_bpt_unload_fn(T) unload, \ + t_bpt_addrcmp_fn(T) addrcmp, \ + t_bpt_keycmp_fn(T) keycmp, \ + t_bpt_valcmp_fn(T) valcmp, \ + t_bpt_cb_fn(T) callback, \ + void *data, \ + t_bpt_unused(T) *unused) \ +{ \ + t_bpt_imm(T) root; \ + t_bpt_addr(T) addr; \ + \ + if (bpt_check_unused(T, bpt, unused, BPT_OPT_INIT) != 0) \ + { \ + bpt_debug(stderr, "[bptd] bad unused array\n"); \ + return (-1); \ + } \ + \ + bpt->nodesz = nodesz; \ + bpt->ukey = ukey; \ + bpt->uaddr = uaddr; \ + bpt->uval = uval; \ + bpt->nodes = 0; \ + bpt->flags = flags; \ + \ + if (bpt->nodesz < sizeof (t_bpt_head(T))) \ + { \ + bpt_debug(stderr, "[bptd] node size less than header size\n"); \ + return (-1); \ + } \ + \ + bpt->nikeys = (bpt->nodesz - sizeof (t_bpt_head(T))) / \ + sizeof (t_bpt_inentry(T)); \ + bpt->nlkeys = (bpt->nodesz - sizeof (t_bpt_head(T))) / \ + sizeof (t_bpt_lfentry(T)); \ + bpt->height = BPT_INIT_HEIGHT; \ + \ + bpt->data = data; \ + \ + if (bpt->nikeys < BPT_INIT_NIKEYS) \ + { \ + bpt_debug(stderr, "[bptd] number of keys per internal node too low\n"); \ + return (-1); \ + } \ + \ + if (bpt->nlkeys < BPT_INIT_NLKEYS) \ + { \ + bpt_debug(stderr, "[bptd] number of keys per leaf node too low\n"); \ + return (-1); \ + } \ + \ + BPT_RESERVE(bpt, unused, addr); \ + BPT_SET_ROOT(bpt, addr); \ + \ + bpt->load = load; \ + bpt->unload = unload; \ + bpt->addrcmp = addrcmp; \ + bpt->keycmp = keycmp; \ + bpt->valcmp = valcmp; \ + bpt->callback = callback; \ + \ + BPT_LOAD(bpt, &root, BPT_GET_ROOT(bpt)); \ + \ + bpt_make_node(T, bpt, &root, BPT_TYPE_LEAF); \ + \ + BPT_UNLOAD(bpt, &root); \ + \ + return (0); \ +} \ + \ +/* \ + * this function cleans the node marking itself as unused \ + */ \ + \ +int bpt_clean_node_##T(t_bpt(T) *bpt, \ + t_bpt_imm(T) *node, \ + t_bpt_unused(T) *unused) \ +{ \ + t_bpt_ndi(T) nkeys = BPT_NKEYS(T, bpt, node); \ + t_bpt_imm(T) child; \ + t_bpt_ndi(T) i; \ + \ + BPT_RELEASE(bpt, unused, node->addr); \ + \ + if (BPT_GET_HEAD(T, node, type) == BPT_TYPE_LEAF) \ + return (0); \ + \ + for (i = 0; (i < nkeys) && \ + (BPT_KEYCMP(bpt, BPT_GET_ENTRY(T, node, i, _key_), \ + bpt->ukey) != 0); \ + i++) \ + { \ + BPT_LOAD(bpt, &child, BPT_GET_INENTRY(T, node, i, _value_)); \ + \ + if (bpt_clean_node(T, bpt, &child, unused) != 0) \ + { \ + BPT_UNLOAD(bpt, &child); \ + return (-1); \ + } \ + \ + BPT_UNLOAD(bpt, &child); \ + } \ + \ + return (0); \ +} \ + \ +/* \ + * this function cleans the tree, destroying each node. \ + * \ + * the unused variable is finally filled with the addresses of the blocks \ + * composing the tree. \ + */ \ + \ +int bpt_clean_##T(t_bpt(T) *bpt, \ + t_bpt_unused(T) *unused) \ +{ \ + t_bpt_imm(T) root; \ + \ + if (bpt_check_unused(T, bpt, unused, BPT_OPT_CLEAN) != 0) \ + { \ + bpt_debug(stderr, "[bptd] bad unused array\n"); \ + return (-1); \ + } \ + \ + BPT_LOAD(bpt, &root, BPT_GET_ROOT(bpt)); \ + \ + if (bpt_clean_node(T, bpt, &root, unused) != 0) \ + { \ + bpt_debug(stderr, "[bptd] cannot clean the nodes\n"); \ + BPT_UNLOAD(bpt, &root); \ + return (-1); \ + } \ + \ + BPT_UNLOAD(bpt, &root); \ + \ + return (0); \ +} + +#define bpt_make(T, _t_bpt_nodesz_, _t_bpt_ndi_, _t_bpt_uni_, \ + _t_bpt_nodes_, _t_bpt_height_, _t_bpt_addr_, \ + _t_bpt_key_, _t_bpt_value_, _t_bpt_inentry_, \ + _t_bpt_lfentry_, _key_, _value_) \ + bpt_make_types(T, _t_bpt_nodesz_, _t_bpt_ndi_, _t_bpt_uni_, _t_bpt_nodes_, \ + _t_bpt_height_, _t_bpt_addr_, _t_bpt_key_, _t_bpt_value_, \ + _t_bpt_inentry_, _t_bpt_lfentry_) \ + bpt_make_protos(T) \ + bpt_make_functions(T, _key_, _value_) + +#endif diff --git a/kaneton/core/include/capability.h b/kaneton/core/include/capability.h new file mode 100644 index 0000000..c331589 --- /dev/null +++ b/kaneton/core/include/capability.h @@ -0,0 +1,143 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/include/capability.h + * + * created julien quintard [wed jun 6 12:44:04 2007] + * updated julien quintard [thu dec 9 15:09:33 2010] + */ + +#ifndef CORE_CAPABILITY_H +#define CORE_CAPABILITY_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include +#include +#include + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * init sizes for the array data structure set + */ + +#define CAPABILITY_CHILDREN_INITSZ 0x4 + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * a capability + * + * this is the structure manipulated by the programs. + */ + +typedef struct +{ + i_node node; + t_id object; + t_operations operations; + t_id descriptor; + + // machine_data(t_capability); +} t_capability; // XXX s_capability + +/* + * a capability descriptor contains the check field of the generated + * capability, the capability descriptors' identifier of the parent + * capability used to build this capability and finally a set of children + * capabilities generated from this capbility. + */ + +typedef struct +{ + t_id id; + + t_capability capability; + t_uint64 check; + + t_id parent; + i_set children; +} t_capability_descriptor; // XXX s_... + +/* + * the capability manager structure + */ + +typedef struct +{ + i_set descriptors; + + t_error (*f_checksum)(char* data, + t_uint32 size, + t_uint64* res); + + // machine_data(m_capability); +} m_capability; + +/* + * the kernel architecture dependent interface + */ + +typedef struct +{ + /* XXX */ +} d_capability; + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../../core/capability/capability.c + */ + +/* + * ../../core/capability/capability.c + */ + +t_error capability_show(t_id id); + +t_error capability_dump(void); + +t_error capability_reserve(t_id object, + t_operations operations, + t_capability* new); + +t_error capability_release(t_id id); + +t_error capability_restrict(t_id id, + t_operations operations, + t_capability* new); + +t_error capability_invalidate(t_id p, + t_id c); + +t_error capability_exist(t_id id); + +t_error capability_get(t_id id, + t_capability_descriptor** descriptor); + +t_error capability_give(t_id id, + i_node node); + +t_error capability_verify(t_capability* provided); + +t_error capability_initialize(void); + +t_error capability_clean(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/core/include/clock.h b/kaneton/core/include/clock.h new file mode 100644 index 0000000..c22f6c0 --- /dev/null +++ b/kaneton/core/include/clock.h @@ -0,0 +1,138 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/include/clock.h + * + * created julien quintard [wed nov 24 18:52:04 2010] + * updated julien quintard [fri dec 10 21:16:50 2010] + */ + +#ifndef CORE_CLOCK_H +#define CORE_CLOCK_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * the structure of a clock state instance. + */ + +typedef struct +{ + t_uint32 millisecond; + t_uint8 second; + t_uint8 minute; + t_uint8 hour; + + t_uint8 day; + t_uint8 month; + t_uint16 year; +} s_clock; + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include +#include + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function takes a clock structure and computes a unique number + * representing the number of milliseconds elapsed since the birth of Jesus + * Christ. + * + * since this number can be larger than 32-bit, a 64-bit number should be + * used to store it. the type t_clock_unique has been introduced for that + * purpose. + */ + +#define CLOCK_UNIQUE(_clock_) \ + ( \ + { \ + (t_uint64)( \ + (_clock_)->millisecond + \ + (_clock_)->second * 1000 + \ + (_clock_)->minute * 60 * 1000 + \ + (_clock_)->hour * 60 * 60 * 1000 + \ + (_clock_)->day * 24 * 60 * 60 * 1000 + \ + (_clock_)->month * 30 * 60 * 60 * 1000 + \ + (_clock_)->year * 12 * 30 * 60 * 60 * 1000 \ + ); \ + } \ + ) + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * the clock unique representation. + */ + +typedef t_uint64 t_clock_unique; + +/* + * the clock manager's structure. + */ + +typedef struct +{ + machine_data(m_clock); +} m_clock; + +/* + * the clock dispatcher. + */ + +typedef struct +{ + t_error (*clock_show)(s_clock*, + mt_margin); + t_error (*clock_update)(t_uint32); + t_error (*clock_current)(s_clock*); + t_error (*clock_initialize)(void); + t_error (*clock_clean)(void); +} d_clock; + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../../core/clock/clock.c + */ + +/* + * ../../core/clock/clock.c + */ + +t_error clock_show(s_clock* clock, + mt_margin margin); + +t_error clock_update(t_uint32 millisecond); + +t_error clock_current(s_clock* clock); + +t_error clock_initialize(void); + +t_error clock_clean(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/core/include/core.h b/kaneton/core/include/core.h new file mode 100644 index 0000000..51a3017 --- /dev/null +++ b/kaneton/core/include/core.h @@ -0,0 +1,146 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/include/core.h + * + * created julien quintard [wed jun 6 12:22:26 2007] + * updated julien quintard [tue feb 8 22:19:49 2011] + */ + +#ifndef CORE_CORE_H +#define CORE_CORE_H 1 + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * endianness + */ + +#define ENDIAN_LITTLE 1 +#define ENDIAN_BIG 2 + +/* + * booleans + */ + +#define BOOLEAN_TRUE 1 +#define BOOLEAN_FALSE 0 + +/* + * permissions + */ + +#define PERMISSION_READ (1 << 0) +#define PERMISSION_WRITE (1 << 1) +#define PERMISSION_EXEC (1 << 2) + +#define PERMISSION_INVALID (~((PERMISSION_READ) | \ + (PERMISSION_WRITE) | \ + (PERMISSION_EXEC))) + +/* + * fits + */ + +#define FIT_FIRST (1 << 0) +#define FIT_BEST (1 << 1) +#define FIT_NEXT (1 << 2) +#define FIT_WORST (1 << 3) + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * true + */ + +#define CORE_TRUE() \ + return (ERROR_TRUE) + +/* + * false + */ + +#define CORE_FALSE() \ + return (ERROR_FALSE) + +/* + * leave + */ + +#define CORE_LEAVE() \ + { \ + return (ERROR_OK); \ + } + +/* + * escape + */ + +#define CORE_ESCAPE(_format_, _arguments_...) \ + { \ + module_call(report, record, \ + _format_ " (%s:%u)", \ + ##_arguments_, __FUNCTION__, __LINE__); \ + \ + return (ERROR_KO); \ + } + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// [XXX:improvements] re-develop everything below +#include +#include +#include + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../../core/core.c + */ + +/* + * ../../core/core.c + */ + +void kaneton(s_init* init); + +t_error kaneton_spawn(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/core/include/cpu.h b/kaneton/core/include/cpu.h new file mode 100644 index 0000000..73cf249 --- /dev/null +++ b/kaneton/core/include/cpu.h @@ -0,0 +1,118 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/include/cpu.h + * + * created julien quintard [sun jun 3 20:25:39 2007] + * updated julien quintard [fri dec 10 21:17:08 2010] + */ + +#ifndef CORE_CPU_H +#define CORE_CPU_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include +#include +#include + +#include + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * the structure for a CPU object. + */ + +typedef struct +{ + i_cpu id; + + t_timeslice efficiency; + + machine_data(o_cpu); +} o_cpu; + +/* + * the CPU manager. + */ + +typedef struct +{ + i_set cpus; + + machine_data(m_cpu); +} m_cpu; + +/* + * the CPU dispatcher. + */ + +typedef struct +{ + t_error (*cpu_show)(i_cpu, + mt_margin); + t_error (*cpu_dump)(void); + t_error (*cpu_current)(i_cpu*); + t_error (*cpu_select)(i_cpu*); + t_error (*cpu_update)(i_cpu, + t_timeslice); + t_error (*cpu_balance)(void); + t_error (*cpu_migrate)(i_task, + i_cpu); + t_error (*cpu_initialize)(void); + t_error (*cpu_clean)(void); +} d_cpu; + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../../core/cpu/cpu.c + */ + +/* + * ../../core/cpu/cpu.c + */ + +t_error cpu_show(i_cpu id, + mt_margin margin); + +t_error cpu_dump(void); + +t_error cpu_current(i_cpu* id); + +t_error cpu_multiprocessor(void); + +t_error cpu_select(i_cpu* id); + +t_error cpu_update(i_cpu id, + t_timeslice timeslice); + +t_error cpu_balance(void); + +t_error cpu_migrate(i_task task, + i_cpu cpu); + +t_error cpu_exist(i_cpu id); + +t_error cpu_get(i_cpu id, + o_cpu** object); + +t_error cpu_initialize(void); + +t_error cpu_clean(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/core/include/error.h b/kaneton/core/include/error.h new file mode 100644 index 0000000..72d9657 --- /dev/null +++ b/kaneton/core/include/error.h @@ -0,0 +1,75 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/include/error.h + * + * created julien quintard [wed jun 6 13:02:28 2007] + * updated julien quintard [fri dec 10 11:33:10 2010] + */ + +#ifndef CORE_ERROR_H +#define CORE_ERROR_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * the error type + */ + +typedef t_sint32 t_error; + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * these general-purpose macros are used to notify the success/failure + * of a function. + */ + +#define ERROR_OK 42 +#define ERROR_KO 666 + +/* + * these macros are used to notify the character of a component. + */ + +#define ERROR_TRUE 1 +#define ERROR_FALSE 0 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * the assert() macro-function is the last-resort error handling tool + * as, should the assertion fail, the kernel will end up in an infinite + * loop. + */ + +#define assert(_test_) \ + if (!(_test_)) \ + { \ + module_call(report, dump); \ + \ + module_call(console, message, \ + '!', "assertion failed in '%s' (%s:%u)\n", \ + #_test_, __FUNCTION__, __LINE__); \ + \ + while (1) \ + ; \ + } + +#endif diff --git a/kaneton/core/include/event.h b/kaneton/core/include/event.h new file mode 100644 index 0000000..cc51b23 --- /dev/null +++ b/kaneton/core/include/event.h @@ -0,0 +1,193 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/include/event.h + * + * created julien quintard [wed jun 6 13:13:41 2007] + * updated julien quintard [wed mar 2 20:27:04 2011] + */ + +#ifndef CORE_EVENT_H +#define CORE_EVENT_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include +#include +#include + +#include + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * the type of the event handler. + */ + +#define EVENT_TYPE_FUNCTION 0 +#define EVENT_TYPE_MESSAGE 1 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function casts a given routine pointer on a proper + * event handler union type. + */ + +#define EVENT_ROUTINE(_routine_) \ + ((u_event_handler)(t_event_routine)(_routine_)) + +/* + * this macro-function casts a task identifier into a proper event + * handler union type. + */ + +#define EVENT_TASK(_task_) \ + ((u_event_handler)(i_task)(_task_)) + +/* + * this macro-function casts an arbitrary data into a valid event data. + */ + +#define EVENT_DATA(_data_) \ + (t_data)((_data_)) + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * intra-kernel event handler type. + */ + +typedef void (*t_event_routine)(i_event, + t_data); + +/* + * message object for extra-kernel events i.e message notification + * sent to a task. + */ + +typedef struct +{ + i_event id; + + t_data data; + + machine_data(o_event_message); +} o_event_message; + +/* + * general-purpose event handler type. + */ + +typedef union +{ + t_event_routine routine; + i_task task; +} u_event_handler; + +/* + * the event object structure. + * + * note that the data attribute represents data passed to the handler + * once triggered. + */ + +typedef struct +{ + i_event id; + + t_type type; + + u_event_handler handler; + t_data data; + + machine_data(o_event); +} o_event; + +/* + * event manager. + */ + +typedef struct +{ + i_set events; + + machine_data(m_event); +} m_event; + +/* + * the event dispatcher. + */ + +typedef struct +{ + t_error (*event_show)(i_event, + mt_margin); + t_error (*event_dump)(void); + t_error (*event_notify)(i_event); + t_error (*event_enable)(void); + t_error (*event_disable)(void); + t_error (*event_reserve)(i_event, + t_type, + u_event_handler, + t_data); + t_error (*event_release)(i_event); + t_error (*event_initialize)(void); + t_error (*event_clean)(void); +} d_event; + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../../core/event/event.c + */ + +/* + * ../../core/event/event.c + */ + +t_error event_show(i_event id, + mt_margin margin); + +t_error event_dump(void); + +t_error event_notify(i_event id); + +t_error event_enable(void); + +t_error event_disable(void); + +t_error event_reserve(i_event id, + t_type type, + u_event_handler handler, + t_data data); + +t_error event_release(i_event id); + +t_error event_exist(i_event id); + +t_error event_get(i_event id, + o_event** object); + +t_error event_initialize(void); + +t_error event_clean(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/core/include/id.h b/kaneton/core/include/id.h new file mode 100644 index 0000000..91546e1 --- /dev/null +++ b/kaneton/core/include/id.h @@ -0,0 +1,128 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/include/id.h + * + * created julien quintard [wed jun 6 12:50:13 2007] + * updated julien quintard [fri dec 10 16:08:01 2010] + */ + +#ifndef CORE_ID_H +#define CORE_ID_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include +#include + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * this macro represents an unused/invalid identifier. + */ + +#define ID_UNUSED ((t_id)-1) + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * general-purpose identifier + */ + +typedef t_uint64 t_id; + +/* + * identifiers for the fundamental kaneton objects. + */ + +typedef t_id i_as; +typedef t_id i_cell; +typedef t_id i_cpu; +typedef t_id i_event; +typedef t_id i_port; +typedef t_id i_region; +typedef t_id i_segment; +typedef t_id i_set; +typedef t_id i_task; +typedef t_id i_thread; +typedef t_id i_timer; + +/* + * a node represents a communicating task in the kayou distributed system. + * + * such a task is first identified by its machine, and then by the task + * identifier itself. + */ + +typedef struct +{ + i_cell cell; + i_task task; +} i_node; + +/* + * the identifier object used to generate manager-specific identifier. + */ + +typedef struct +{ + t_id id; +} o_id; + +/* + * the identifier manager. + */ + +typedef struct +{ +} m_id; + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../../core/id/id.c + */ + +/* + * ../../core/id/id.c + */ + +t_error id_show(o_id* object, + mt_margin margin); + +t_error id_reserve(o_id* object, + t_id* id); + +t_error id_release(o_id* object, + t_id id); + +t_error id_build(o_id* object); + +t_error id_destroy(o_id* object); + +t_error id_initialize(void); + +t_error id_clean(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/core/include/init.h b/kaneton/core/include/init.h new file mode 100644 index 0000000..331925e --- /dev/null +++ b/kaneton/core/include/init.h @@ -0,0 +1,201 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/include/init.h + * + * created julien quintard [wed jun 6 13:20:24 2007] + * updated julien quintard [fri jan 14 22:43:03 2011] + */ + +#ifndef CORE_INIT_H +#define CORE_INIT_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include +#include +#include + +#include + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * the structure for an input i.e a server that must be launched at startup. + */ + +typedef struct +{ + char* name; + t_psize size; +} s_init_input; + +/* + * the header of the inputs array containing the number of entries. + */ + +typedef struct +{ + t_uint32 ninputs; +} s_init_inputs; + +/* + * the segment structure description. + */ + +typedef struct +{ + t_paddr address; + t_psize size; + t_permissions permissions; + t_options options; +} s_init_segment; + +/* + * the region structure description. + * + * the 'segment' field represents an index in the array of segments. + */ + +typedef struct +{ + t_uint32 segment; + + t_vaddr address; + t_paddr offset; + t_vsize size; + t_options options; +} s_init_region; + +/* + * the CPU structure description. + */ + +typedef struct +{ + i_cpu cpu; +} s_init_cpu; + +/* + * the init structure. + * + * the 'pbase' and 'psize' represent the base and size of the underlying + * physical memory layout. + * + * the 'kcode' and 'kcodesz' represent the location of the kernel code + * in the physical memory along with its size. + * + * the 'scode' and 'scodesz' represent the location of the very first + * server to spawn, the 'system' service, in the physical memory along with + * its size. in addition, the 'slocation' indicates the address in the + * virtual memory where the server's code should be mapped. finally, the + * 'sentry' indicates the virtual address of the entry point for the server's + * code. + * + * the 'init' and 'initsz' represent the physical location and size of the + * whole init structure. indeed, the init structure is passed to the kernel + * in as a package i.e packed in a single area of memory. this meory area + * must be taken care of in order to prevent it to be erased for instance. + * + * the 'inputs' and 'inputssz' represent the physical location and size + * of the array containing the inputs i.e the servers to be spawn at + * the startup by the _system_ server. + * + * the 'nsegments' indicates the number of segments registered in the + * array defined by 'segments' and 'segmentssz'. the segment registered + * in this array must be considered by the kernel as being allocated. + * therefore, the kernel should add these segments to its own data structures + * as soon as its memory managers have been initialized. in more practical + * terms, such segments may reference memory mapped devices such as the + * DMA - Direct Memory Access memory area, the kernel code area, the + * init structure area, the inputs array, the array of segments itself, + * the kernel stack, and so on and so forth. + * + * the 'nregions', 'regions' and 'regionssz' provide the same kind of + * array but for pre-reserved regions. such regions include the kernel + * code and stack which must obviously be mapped etc. + * + * the 'ncpus', 'cpus' and 'cpussz' indicate an array containing the + * descriptions of the CPUs present on the system i.e for multi-processor + * systems. on a mono-processor system, the array is obviously composed + * of a single entry i.e _ncpus_ equals one. finally, the 'bsp' indicates + * the identifier of CPU acting as the BSP - Boot Strap Processor i.e + * the initally running processor. note that the 'bsp' does not provides + * an index in the array of CPUs but the kaneton identifier of the BSP. + * + * the 'kstack' and 'kstacksz' indicate the physical location and size of the + * kernel stack. + * + * finally, the 'alloc' and 'allocsz' indicate the virtual location of + * a memory area to be used by the kaneton fine-grained memory allocator + * until the kernel's memory manager have been set up. indeed, the kaneton + * design relies on sets for managing data structures. the memory managers + * such as the segment, region and as managers rely on the set manager for + * storing their information. therefore, the memory manager rely on another + * manager for storing their data while the set manager relies on malloc() + * for allocating memory. and obviously, malloc() relies on the memory + * managers for retrieving additional memory when it runs out of space. + * this infinite loop can be avoided by providing malloc() a temporary + * zone of allocation i.e the pre-allocated memory area referenced by + * 'alloc' and 'allocsz'. + * + * note that the kernel expects to receive a sorted array of segments and + * regions, the segment[0] and region[0] being the lowest in memory. + * + * besides, note that the size fields represent sizes aligned on + * ___kaneton$pagesz. + */ + +typedef struct +{ + t_paddr pbase; + t_psize psize; + + t_paddr vbase; + t_psize vsize; + + t_paddr kcode; + t_psize kcodesz; + + t_paddr scode; + t_psize scodesz; + t_vaddr slocation; + t_vaddr sentry; + + t_paddr init; + t_psize initsz; + + s_init_inputs* inputs; + t_psize inputssz; + + t_uint32 nsegments; + s_init_segment* segments; + t_psize segmentssz; + + t_uint32 nregions; + s_init_region* regions; + t_psize regionssz; + + t_uint32 ncpus; + s_init_cpu* cpus; + t_psize cpussz; + i_cpu bsp; + + t_paddr kstack; + t_psize kstacksz; + + t_paddr alloc; + t_psize allocsz; + + machine_data(s_init); +} s_init; + +#endif diff --git a/kaneton/core/include/interface.h b/kaneton/core/include/interface.h new file mode 100644 index 0000000..9c823b4 --- /dev/null +++ b/kaneton/core/include/interface.h @@ -0,0 +1,639 @@ +/* + * ---------- information ----------------------------------------------------- + * + * [XXX:improvements] the whole manager should be re-developed! + */ + +#ifndef CORE_INTERFACE_H +#define CORE_INTERFACE_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include +#include + +#include + +/* + * ---------- macros ---------------------------------------------------------- + */ + +#define INTERFACE_AS_COPY 3 +#define INTERFACE_AS_RELEASE 6 +#define INTERFACE_AS_ATTRIBUTE_TASK 7 +#define INTERFACE_EVENT_RESERVE 8 +#define INTERFACE_EVENT_RELEASE 9 +#define INTERFACE_EVENT_ATTRIBUTE_TYPE 10 +#define INTERFACE_EVENT_ATTRIBUTE_HANDLER 11 +#define INTERFACE_EVENT_ATTRIBUTE_DATA 12 +#define INTERFACE_MAP_RESERVE 23 +#define INTERFACE_MAP_RELEASE 24 +#define INTERFACE_MAP_RESIZE 25 +#define INTERFACE_REGION_RESERVE 26 +#define INTERFACE_REGION_RELEASE 27 +#define INTERFACE_SCHEDULER_QUANTUM 28 +#define INTERFACE_SCHEDULER_YIELD 29 +#define INTERFACE_SCHEDULER_UPDATE 33 +#define INTERFACE_SEGMENT_CLONE 34 +#define INTERFACE_SEGMENT_GIVE 35 +#define INTERFACE_SEGMENT_COPY 36 +#define INTERFACE_SEGMENT_RELEASE 38 +#define INTERFACE_SEGMENT_PERMISSIONS 40 +#define INTERFACE_SEGMENT_ATTRIBUTE_AS 42 +#define INTERFACE_SEGMENT_ATTRIBUTE_ADDRESS 44 +#define INTERFACE_SEGMENT_ATTRIBUTE_SIZE 45 +#define INTERFACE_SEGMENT_ATTRIBUTE_PERMISSIONS 46 +#define INTERFACE_TASK_RESERVE 49 +#define INTERFACE_TASK_RELEASE 50 +#define INTERFACE_TASK_PRIORITY 51 +#define INTERFACE_TASK_ATTRIBUTE_PARENT 54 +#define INTERFACE_TASK_ATTRIBUTE_CLASS 55 +#define INTERFACE_TASK_ATTRIBUTE_BEHAVIOUS 56 +#define INTERFACE_TASK_ATTRIBUTE_PRIORITY 57 +#define INTERFACE_TASK_ATTRIBUTE_AS 58 +#define INTERFACE_TASK_ATTRIBUTE_SCHED 59 +#define INTERFACE_THREAD_RELEASE 64 +#define INTERFACE_THREAD_PRIORITY 65 +#define INTERFACE_THREAD_LOAD 68 +#define INTERFACE_THREAD_STORE 69 +#define INTERFACE_THREAD_ATTRIBUTE_TASK 70 +#define INTERFACE_THREAD_ATTRIBUTE_PRIORITY 71 +#define INTERFACE_THREAD_ATTRIBUTE_STATE 72 +#define INTERFACE_TIMER_RESERVE 76 +#define INTERFACE_TIMER_RELEASE 77 +#define INTERFACE_TIMER_ATTRIBUTE_DELAY 81 +#define INTERFACE_TIMER_ATTRIBUTE_REPEAT 82 +#define INTERFACE_TIMER_ATTRIBUTE_TYPE 83 +#define INTERFACE_TIMER_ATTRIBUTE_HANDLER 84 +#define INTERFACE_TIMER_ATTRIBUTE_DATA 85 + + +#define INTERFACE_NSYSCALLS 86 + +/* + * ---------- types ----------------------------------------------------------- + */ + +typedef struct +{ + t_id id; + union + { + struct + { + t_operations operation; + t_capability capability; + union + { + struct + { + t_id arg1; + t_vaddr arg2; + i_as arg3; + t_vaddr arg4; + t_vsize arg5; + } as_copy; + struct + { + t_id arg1; + } as_release; + struct + { + t_id arg1; + } as_attribute_task; + struct + { + t_id arg1; + t_type arg2; + u_event_handler arg3; + t_vaddr arg4; + } event_reserve; + struct + { + t_id arg1; + } event_release; + struct + { + t_id arg1; + } event_attribute_type; + struct + { + t_id arg1; + } event_attribute_handler; + struct + { + t_id arg1; + } event_attribute_data; + struct + { + t_id arg1; + i_task arg2; + t_uint8 arg3; + } io_grant; + struct + { + t_id arg1; + i_task arg2; + t_uint8 arg3; + } io_deny; + struct + { + i_task arg1; + t_id arg2; + } io_read_8; + struct + { + i_task arg1; + t_id arg2; + } io_read_16; + struct + { + i_task arg1; + t_id arg2; + } io_read_32; + struct + { + i_task arg1; + t_id arg2; + } io_read_64; + struct + { + i_task arg1; + t_id arg2; + t_uint8 arg3; + } io_write_8; + struct + { + i_task arg1; + t_id arg2; + t_uint16 arg3; + } io_write_16; + struct + { + i_task arg1; + t_id arg2; + t_uint32 arg3; + } io_write_32; + struct + { + i_task arg1; + t_id arg2; + t_uint64 arg3; + } io_write_64; + struct + { + t_id arg1; + t_options arg2; + t_vsize arg3; + t_permissions arg4; + } map_reserve; + struct + { + t_id arg1; + t_vaddr arg2; + } map_release; + struct + { + t_id arg1; + t_vaddr arg2; + t_vsize arg3; + } map_resize; + struct + { + t_id arg1; + i_segment arg2; + t_paddr arg3; + t_options arg4; + t_vaddr arg5; + t_vsize arg6; + } region_reserve; + struct + { + i_as arg1; + t_id arg2; + } region_release; + struct + { + t_quantum arg1; + } scheduler_quantum; + struct + { + i_cpu arg1; + } scheduler_yield; + struct + { + i_thread arg1; + } scheduler_update; + struct + { + i_as arg1; + t_id arg2; + } segment_clone; + struct + { + i_as arg1; + t_id arg2; + } segment_give; + struct + { + t_id arg1; + t_paddr arg2; + i_segment arg3; + t_paddr arg4; + t_psize arg5; + } segment_copy; + struct + { + i_segment arg1; + } segment_release; + struct + { + t_id arg1; + t_permissions arg2; + } segment_permissions; + struct + { + t_id arg1; + } segment_attribute_as; + struct + { + t_id arg1; + } segment_attribute_address; + struct + { + t_id arg1; + } segment_attribute_size; + struct + { + t_id arg1; + } segment_attribute_permissions; + struct + { + t_class arg1; + t_behaviour arg2; + t_priority arg3; + } task_reserve; + struct + { + t_id arg1; + } task_release; + struct + { + t_id arg1; + t_priority arg2; + } task_priority; + struct + { + t_id arg1; + } task_attribute_parent; + struct + { + t_id arg1; + } task_attribute_class; + struct + { + t_id arg1; + } task_attribute_behaviour; + struct + { + t_id arg1; + } task_attribute_priority; + struct + { + t_id arg1; + } task_attribute_as; + struct + { + t_id arg1; + } task_attribute_sched; + struct + { + t_id arg1; + } thread_release; + struct + { + t_id arg1; + t_priority arg2; + } thread_priority; + struct + { + t_id arg1; + s_thread_context arg2; + } thread_load; + struct + { + t_id arg1; + } thread_store; + struct + { + t_id arg1; + } thread_attribute_task; + struct + { + t_id arg1; + } thread_attribute_priority; + struct + { + t_id arg1; + } thread_attribute_state; + struct + { + t_type arg1; + u_timer_handler arg2; + t_vaddr arg3; + t_uint32 arg4; + t_uint32 arg5; + } timer_reserve; + struct + { + t_id arg1; + } timer_release; + struct + { + t_id arg1; + } timer_attribute_delay; + struct + { + t_id arg1; + } timer_attribute_repeat; + struct + { + t_id arg1; + } timer_attribute_type; + struct + { + t_id arg1; + } timer_attribute_handler; + struct + { + t_id arg1; + } timer_attribute_data; + } u; + } request; + struct + { + t_error error; + union + { + struct + { + i_task result1; + } as_attribute_task; + struct + { + t_type result1; + } event_attribute_type; + struct + { + u_event_handler result1; + } event_attribute_handler; + struct + { + t_vaddr result1; + } event_attribute_data; + struct + { + t_uint8 result1; + } io_read_8; + struct + { + t_uint16 result1; + } io_read_16; + struct + { + t_uint32 result1; + } io_read_32; + struct + { + t_uint64 result1; + } io_read_64; + struct + { + t_vaddr result1; + } map_reserve; + struct + { + t_vaddr result1; + } map_resize; + struct + { + i_region result1; + } region_reserve; + struct + { + i_segment result1; + } segment_clone; + struct + { + i_as result1; + } segment_attribute_as; + struct + { + t_paddr result1; + } segment_attribute_address; + struct + { + t_psize result1; + } segment_attribute_size; + struct + { + t_permissions result1; + } segment_attribute_permissions; + struct + { + i_task result1; + } task_reserve; + struct + { + i_task result1; + } task_attribute_parent; + struct + { + t_class result1; + } task_attribute_class; + struct + { + t_behaviour result1; + } task_attribute_behaviour; + struct + { + t_priority result1; + } task_attribute_priority; + struct + { + i_as result1; + } task_attribute_as; + struct + { + t_state result1; + } task_attribute_sched; + struct + { + s_thread_context result1; + } thread_store; + struct + { + i_task result1; + } thread_attribute_task; + struct + { + t_priority result1; + } thread_attribute_priority; + struct + { + t_state result1; + } thread_attribute_state; + struct + { + i_timer result1; + } timer_reserve; + struct + { + t_uint32 result1; + } timer_attribute_delay; + struct + { + t_uint32 result1; + } timer_attribute_repeat; + struct + { + t_type result1; + } timer_attribute_type; + struct + { + u_timer_handler result1; + } timer_attribute_handler; + struct + { + t_vaddr result1; + } timer_attribute_data; + } u; + } reply; + } u; +} o_syscall; + +typedef t_error (*t_interface_dispatch)(o_syscall*); + + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../../core/interface/interface.c + */ + +/* + * ../../core/interface/interface.c + */ + +t_error interface_as_copy(o_syscall* message); + +t_error interface_as_release(o_syscall* message); + +t_error interface_as_attribute_task(o_syscall* message); + +t_error interface_event_reserve(o_syscall* message); + +t_error interface_event_release(o_syscall* message); + +t_error interface_event_attribute_type(o_syscall* message); + +t_error interface_event_attribute_handler(o_syscall* message); + +t_error interface_event_attribute_data(o_syscall* message); + +t_error interface_map_reserve(o_syscall* message); + +t_error interface_map_release(o_syscall* message); + +t_error interface_map_resize(o_syscall* message); + +t_error interface_region_reserve(o_syscall* message); + +t_error interface_region_release(o_syscall* message); + +t_error interface_scheduler_quantum(o_syscall* message); + +t_error interface_scheduler_yield(o_syscall* message); + +t_error interface_scheduler_update(o_syscall* message); + +t_error interface_segment_clone(o_syscall* message); + +t_error interface_segment_give(o_syscall* message); + +t_error interface_segment_copy(o_syscall* message); + +t_error interface_segment_release(o_syscall* message); + +t_error interface_segment_permissions(o_syscall* message); + +t_error interface_segment_attribute_as(o_syscall* message); + +t_error interface_segment_attribute_address(o_syscall* message); + +t_error interface_segment_attribute_size(o_syscall* message); + +t_error interface_segment_attribute_permissions(o_syscall* message); + +t_error interface_task_reserve(o_syscall* message); + +t_error interface_task_release(o_syscall* message); + +t_error interface_task_priority(o_syscall* message); + +t_error interface_task_attribute_parent(o_syscall* message); + +t_error interface_task_attribute_class(o_syscall* message); + +t_error interface_task_attribute_behaviour(o_syscall* message); + +t_error interface_task_attribute_priority(o_syscall* message); + +t_error interface_task_attribute_as(o_syscall* message); + +t_error interface_task_attribute_sched(o_syscall* message); + +t_error interface_thread_release(o_syscall* message); + +t_error interface_thread_priority(o_syscall* message); + +t_error interface_thread_load(o_syscall* message); + +t_error interface_thread_store(o_syscall* message); + +t_error interface_thread_attribute_task(o_syscall* message); + +t_error interface_thread_attribute_priority(o_syscall* message); + +t_error interface_thread_attribute_state(o_syscall* message); + +t_error interface_timer_reserve(o_syscall* message); + +t_error interface_timer_release(o_syscall* message); + +t_error interface_timer_attribute_delay(o_syscall* message); + +t_error interface_timer_attribute_repeat(o_syscall* message); + +t_error interface_timer_attribute_type(o_syscall* message); + +t_error interface_timer_attribute_handler(o_syscall* message); + +t_error interface_timer_attribute_data(o_syscall* message); + +t_error interface_notify(t_uint8* buffer, + t_vsize size, + i_node source); + +t_error interface_initialize(void); + +t_error interface_clean(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/core/include/io.h b/kaneton/core/include/io.h new file mode 100644 index 0000000..88e6414 --- /dev/null +++ b/kaneton/core/include/io.h @@ -0,0 +1,129 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/include/io.h + * + * created julien quintard [wed jun 6 13:26:01 2007] + * updated julien quintard [sun dec 19 17:31:50 2010] + */ + +#ifndef CORE_IO_H +#define CORE_IO_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include +#include +#include + +#include + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * these macros defines the width of the I/O operations. + */ + +#define IO_WIDTH_8 (1 << 0) +#define IO_WIDTH_16 (1 << 1) +#define IO_WIDTH_32 (1 << 2) +#define IO_WIDTH_64 (1 << 3) + +/* + * ---------- macro-functions ------------------------------------------------- + */ + +/* + * this macro-function is used to cast any pointer to a generic + * input/output. + * + * this is required since the I/O manager's functions handle all + * types through a single routine. + */ + +#define IO_INPUT(_value_) ((t_uint64)_value_) +#define IO_OUTPUT(_pointer_) ((void*)_pointer_) + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * the I/O manager structure. + */ + +typedef struct +{ + machine_data(m_io); +} m_io; + +/* + * the I/O dispatcher. + */ + +typedef struct +{ + t_error (*io_grant)(i_task, + i_port, + t_width); + t_error (*io_deny)(i_task, + i_port, + t_width); + t_error (*io_read)(i_task, + i_port, + t_width, + void*); + t_error (*io_write)(i_task, + i_port, + t_width, + t_uint64); + t_error (*io_initialize)(void); + t_error (*io_clean)(void); +} d_io; + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../../core/io/io.c + */ + +/* + * ../../core/io/io.c + */ + +t_error io_grant(i_task task, + i_port port, + t_width width); + +t_error io_deny(i_task task, + i_port port, + t_width width); + +t_error io_read(i_task task, + i_port port, + t_width width, + void* data); + +t_error io_write_8(i_task task, + i_port port, + t_width width, + t_uint64 data); + +t_error io_initialize(void); + +t_error io_clean(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/core/include/kernel.h b/kaneton/core/include/kernel.h new file mode 100644 index 0000000..a66e4e3 --- /dev/null +++ b/kaneton/core/include/kernel.h @@ -0,0 +1,97 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/include/kernel.h + * + * created julien quintard [wed jun 6 13:27:34 2007] + * updated julien quintard [fri dec 10 21:18:18 2010] + */ + +#ifndef CORE_KERNEL_H +#define CORE_KERNEL_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include +#include +#include + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * the kernel manager's structure. + * + * this structure contains the kernel's machine, task, address space and + * thread identifiers. finally the node identifier is also included though + * based on the previous identifiers. + * + * indeed, the machine identifier is a random number generated at the boot + * time. since this is a 64-bit number, the chances of machine identifiers + * collisions inside a small distributed system (less than a thousand + * machines) are relatively low. + * + * the task, address space and thread identifiers are used to retrieve the + * respective kernel objects. + * + * finally, the node identifier is used to identify a task within the + * distributed system. this identifier is for instance used to send messages + * to task of other machines of the network. in other words, every task + * can be identified either by its task identifier on the machine or by + * its node identifier in the network. since the kernel is a task very + * much like the others, it is also reachable through its node identifier + * which is included in the kernel manager's structure below. + */ + +typedef struct +{ + i_cell cell; + i_task task; + i_as as; + i_thread thread; + + i_node node; + + machine_data(m_kernel); +} m_kernel; + +/* + * the kernel dispatcher. + */ + +typedef struct +{ + t_error (*kernel_dump)(void); + t_error (*kernel_initialize)(void); + t_error (*kernel_clean)(void); +} d_kernel; + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../../core/kernel/kernel.c + */ + +/* + * ../../core/kernel/kernel.c + */ + +t_error kernel_dump(void); + +t_error kernel_initialize(void); + +t_error kernel_clean(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/core/include/map.h b/kaneton/core/include/map.h new file mode 100644 index 0000000..0cba711 --- /dev/null +++ b/kaneton/core/include/map.h @@ -0,0 +1,108 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/include/map.h + * + * created julien quintard [wed jun 6 13:31:27 2007] + * updated julien quintard [fri jan 14 20:18:43 2011] + */ + +#ifndef CORE_MAP_H +#define CORE_MAP_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include +#include +#include + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * the flags that can be passed at the map reservation time. + * + * for more information, please refer to the region-related flags. + */ + +#define MAP_OPTION_NONE 0 +#define MAP_OPTION_FORCE (1 << 0) +#define MAP_OPTION_SYSTEM (1 << 1) + +#define MAP_OPTION_INVALID (~(MAP_OPTION_FORCE | MAP_OPTION_SYSTEM)) + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * the map manager structure. + */ + +typedef struct +{ + machine_data(m_map); +} m_map; + +/* + * the map dispatcher. + */ + +typedef struct +{ + t_error (*map_reserve)(i_as, + t_options, + t_vsize, + t_permissions, + t_vaddr*); + t_error (*map_release)(i_as, + t_vaddr); + t_error (*map_resize)(i_as, + t_vaddr, + t_vsize, + t_vaddr*); + t_error (*map_initialize)(void); + t_error (*map_clean)(void); +} d_map; + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../../core/map/map.c + */ + +/* + * ../../core/map/map.c + */ + +t_error map_reserve(i_as as, + t_options options, + t_vsize size, + t_permissions permissions, + t_vaddr* address); + +t_error map_release(i_as as, + t_vaddr address); + +t_error map_resize(i_as as, + t_vaddr old, + t_vsize size, + t_vaddr* new); + +t_error map_initialize(void); + +t_error map_clean(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/core/include/message.h b/kaneton/core/include/message.h new file mode 100644 index 0000000..b0376b6 --- /dev/null +++ b/kaneton/core/include/message.h @@ -0,0 +1,246 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/include/message.h + * + * created julien quintard [wed jun 6 13:34:19 2007] + * updated julien quintard [fri dec 17 16:05:04 2010] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * [XXX:improvements] the whole manager should be re-developed! + */ + +#ifndef CORE_MESSAGE_H +#define CORE_MESSAGE_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include +#include +#include + +#include + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * values for completed member + */ + +#define MESSAGE_REQUEST_PENDING 0 +#define MESSAGE_REQUEST_COMPLETED 1 + +/* + * predefined message types + */ + +#define MESSAGE_TYPE_INTERFACE 0 +#define MESSAGE_TYPE_EVENT 1 +#define MESSAGE_TYPE_TIMER 2 + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * message copying request + */ + +typedef struct +{ + t_status completed; +} s_message_request; + +/* + * message object + */ + +typedef struct +{ + t_id id; + + i_as as; + i_thread blocked; + void* data; + t_vsize size; + i_node sender; + + machine_data(o_message); +} o_message; + +/* + * message type object + */ + +typedef struct +{ + t_id type; + + t_id id; + + t_vsize size; + + i_set queue; + i_set waiters; + + machine_data(o_message_type); +} o_message_type; + +/* + * message manager + */ + +typedef struct +{ + machine_data(m_message); +} m_message; + +/* + * the message architecture-dependent interface + */ + +typedef struct +{ + t_error (*message_register)(i_task, + t_type, + t_vsize); + t_error (*message_flush)(i_task); + t_error (*message_send)(i_task, + i_node, + t_type, + t_vaddr, + t_vsize); + t_error (*message_transmit)(i_task, + i_node, + t_type, + t_vaddr, + t_vsize); + t_error (*message_throw)(i_task, + i_node, + t_type, + t_vaddr, + t_vsize, + s_message_request*); + t_error (*message_receive)(i_task, + t_type, + t_vaddr, + t_vsize*, + i_node*); + t_error (*message_grab)(i_task, + t_type, + t_vaddr, + s_message_request*); + t_error (*message_poll)(i_task, + t_type, + t_vaddr, + t_vsize*, + i_node*); + t_error (*message_state)(i_task, + s_message_request); + t_error (*message_wait)(i_task, + s_message_request, + t_vsize*, + i_node*); + t_error (*message_return)(i_thread, + t_error); + t_error (*message_return_info)(i_thread, + t_error, + t_vsize, + i_node); + t_error (*message_initialize)(void); + t_error (*message_clean)(void); +} d_message; + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../../core/message/message.c + */ + +/* + * ../../core/message/message.c + */ + +t_error message_register(i_task task, + t_type type, + t_vsize size); + +t_error message_size(i_task task, + t_type type, + t_vsize* size); + +t_error message_flush(i_task task); + +t_error message_send(i_task task, + i_node destination, + t_type type, + t_vaddr data, + t_vsize size); + +t_error message_transmit(i_task task, + i_node destination, + t_type type, + t_vaddr data, + t_vsize size); + +t_error message_throw(i_task task, + i_node destination, + t_type type, + t_vaddr data, + t_vsize size, + s_message_request* request); + +t_error message_receive(i_task task, + t_type type, + t_vaddr data, + t_vsize* size, + i_node* sender); + +t_error message_grab(i_task task, + t_type type, + t_vaddr data, + s_message_request* request); + +t_error message_poll(i_task task, + t_type type, + t_vaddr data, + t_vsize* size, + i_node* sender); + +t_error message_state(i_task task, + s_message_request request); + +t_error message_wait(i_task task, + s_message_request request, + t_vsize* size, + i_node* sender); + +t_error message_return(i_thread thread, + t_error code); + +t_error message_return_info(i_thread thread, + t_error code, + t_vsize size, + i_node sender); + +t_error message_initialize(void); + +t_error message_clean(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/core/include/region.h b/kaneton/core/include/region.h new file mode 100644 index 0000000..160afbc --- /dev/null +++ b/kaneton/core/include/region.h @@ -0,0 +1,285 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/include/region.h + * + * created julien quintard [wed jun 6 13:40:54 2007] + * updated julien quintard [fri apr 8 09:04:13 2011] + */ + +#ifndef CORE_REGION_H +#define CORE_REGION_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include +#include +#include + +#include + +/* + * ---------- algorithms ------------------------------------------------------ + */ + +/* + * the several algorithms supported by the region manager. + * + * note that such algorithms can be activated through the REGION_ALGORITHM + * macro. + */ + +#define REGION_ALGORITHM_FIT_FIRST (1 << 0) +#define REGION_ALGORITHM_FIT_BEST (1 << 1) + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * the flags which can be used for reserving regions. + * + * the _force_ option enables the caller of region_reserve() to specify + * the address of reservation. + */ + +#define REGION_OPTION_NONE 0 +#define REGION_OPTION_FORCE (1 << 0) + +#define REGION_OPTION_INVALID (~(REGION_OPTION_FORCE)) + +/* + * this macro defines the size of the vault i.e the set of regions + * which must be temporarily prevented from being reserved. + */ + +#define REGION_VAULT_SIZE 16 + +/* + * these macros indicate the state of a vault entry. + */ + +#define REGION_VAULT_STATE_AVAILABLE 0 +#define REGION_VAULT_STATE_USED 1 + +/* + * ---------- macro-functions ------------------------------------------------- + */ + +/* + * this macro-function computes a region identifier based on the address. + */ + +#define REGION_IDENTIFIER(_object_) \ + (i_region)((_object_)->address / ___kaneton$pagesz) + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * this structure represents a memory area which needs to be prevented + * from being reserved. + */ + +typedef struct +{ + t_state state; + i_as as; + t_vaddr address; + t_vsize size; +} s_region_zone; + +/* + * the region object structure. + * + * a region represents an area of virtual memory and is identified by + * a region identifier. + * + * every region maps a _segment_ at a given _offset_ and for a certain + * _size_. the resulting virtual address _address_ can then be used to + * access the segment through the virtual memory mechanism. + * + * the _options_ are used to parameterize both the region's behaviour + * as well as the way the region is reserved. for more information, please + * refer to the region_reserve() function. + */ + +typedef struct +{ + i_region id; + + i_segment segment; + t_paddr offset; + + t_vaddr address; + t_vsize size; + t_options options; + + machine_data(o_region); +} o_region; + +/* + * the region manager structure. + * + * the _base_ attribute represents the lower bound virtual address while + * the _size_ represents the number of bytes the virtual memory is composed + * of. + * + * the _vault_ set identifier references a set of regions which need + * to be prevented from being reserved until the current operation finishes. + */ + +typedef struct +{ + t_vaddr base; + t_vsize size; + + s_region_zone vault[REGION_VAULT_SIZE]; + + machine_data(m_region); +} m_region; + +/* + * the region dispatcher. + */ + +typedef struct +{ + t_error (*region_show)(i_as, + i_region, + mt_margin); + t_error (*region_dump)(void); + t_error (*region_protect)(i_as, + t_vaddr, + t_vsize); + t_error (*region_unprotect)(i_as, + t_vaddr, + t_vsize); + t_error (*region_inject)(i_as, + o_region*, + i_region*); + t_error (*region_split)(i_as, + i_region, + t_vsize, + i_region*, + i_region*); + t_error (*region_resize)(i_as, + i_region, + t_vsize, + i_region*); + t_error (*region_coalesce)(i_as, + i_region, + i_region, + i_region*); + t_error (*region_reserve)(i_as, + i_segment, + t_paddr, + t_options, + t_vaddr, + t_vsize, + i_region*); + t_error (*region_release)(i_as, + i_region); + t_error (*region_flush)(i_as); + t_error (*region_initialize)(t_vaddr, + t_vsize); + t_error (*region_clean)(void); +} d_region; + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../../core/region/region-fit.c + */ + +/* + * ../../core/region/region-fit.c + */ + +t_error region_show(i_as asid, + i_region regid, + mt_margin margin); + +t_error region_dump(void); + +t_error region_protect(i_as as, + t_vaddr address, + t_vsize size); + +t_error region_unprotect(i_as as, + t_vaddr address, + t_vsize size); + +t_error region_protected(i_as as, + t_vaddr address); + +t_error region_fit_first(i_as asid, + t_vsize size, + t_vaddr* address); + +t_error region_space(i_as asid, + t_vsize size, + t_vaddr* address); + +t_error region_inject(i_as as, + o_region* object, + i_region* id); + +t_error region_split(i_as asid, + i_region regid, + t_vsize size, + i_region* left, + i_region* right); + +t_error region_resize(i_as as, + i_region old, + t_vsize size, + i_region* new); + +t_error region_coalesce(i_as asid, + i_region left, + i_region right, + i_region* region); + +t_error region_reserve(i_as asid, + i_segment segid, + t_paddr offset, + t_options options, + t_vaddr address, + t_vsize size, + i_region* region); + +t_error region_release(i_as asid, + i_region regid); + +t_error region_locate(i_as as, + t_vaddr address, + i_region* id); + +t_error region_flush(i_as asid); + +t_error region_exist(i_as asid, + i_region regid); + +t_error region_get(i_as asid, + i_region regid, + o_region** object); + +t_error region_initialize(t_vaddr base, + t_vsize size); + +t_error region_clean(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/core/include/scheduler.h b/kaneton/core/include/scheduler.h new file mode 100644 index 0000000..0fcbdc6 --- /dev/null +++ b/kaneton/core/include/scheduler.h @@ -0,0 +1,290 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...kaneton/kaneton/core/include/scheduler.h + * + * created julien quintard [wed jun 6 13:44:48 2007] + * updated julien quintard [sat feb 5 16:59:18 2011] + */ + +#ifndef CORE_SCHEDULER_H +#define CORE_SCHEDULER_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include +#include +#include + +#include + +/* + * ---------- algorithms ------------------------------------------------------ + */ + +/* + * the supported scheduler algorithms. + */ + +#define SCHEDULER_ALGORITHM_MFQ (1 << 0) + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * the scheduler state either started or stopped. + */ + +#define SCHEDULER_STATE_START 1 +#define SCHEDULER_STATE_STOP 2 + +/* + * initial value for the scheduler quantum in milliseconds. + */ + +#define SCHEDULER_QUANTUM TIMER_DELAY + +/* + * the number of priorities i.e the number of queues. + */ + +#define SCHEDULER_NPRIORITIES 60 + +/* + * timeslice bounds. + */ + +#define SCHEDULER_TIMESLICE_HIGH 250 +#define SCHEDULER_TIMESLICE_LOW 10 + +/* + * the timeslice granularity. + */ + +#define SCHEDULER_GRANULARITY _scheduler.quantum + +/* + * scheduling priorities. + */ + +#define SCHEDULER_PRIORITY_HIGH SCHEDULER_NPRIORITIES - 1 +#define SCHEDULER_PRIORITY_LOW 0 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function computes the thread's high precision character + * by taking into account both the task's and thread's priorities. + */ + +#define SCHEDULER_CHARACTER(_id_) \ + ( \ + { \ + o_task* _task_; \ + o_thread* _thread_; \ + \ + assert(thread_get((_id_), &_thread_) == ERROR_OK); \ + assert(task_get(_thread_->task, &_task_) == ERROR_OK); \ + \ + ((_task_->priority - TASK_PRIORITY_BACKGROUND_LOW) * \ + (_thread_->priority - THREAD_PRIORITY_LOW)); \ + } \ + ) + +/* + * this macro-function computes the priority for a giver thread. this + * is a low precision measurement of a thread's priority which is used + * for locating the proper scheduling queue. + * + * indeed, while the character lies in a large range, it is then + * reduced within the range [SCHEDULER_PRIORITY_LOW, SCHEDULER_PRIORITY_HIGH]. + */ + +#define SCHEDULER_PRIORITY(_thread_) \ + ( \ + { \ + t_priority _character_; \ + \ + _character_ = SCHEDULER_CHARACTER((_thread_)); \ + \ + SCHEDULER_PRIORITY_LOW + \ + ((_character_ * \ + (SCHEDULER_PRIORITY_HIGH - SCHEDULER_PRIORITY_LOW)) / \ + ((TASK_PRIORITY_KERNEL_HIGH - TASK_PRIORITY_BACKGROUND_LOW) * \ + (THREAD_PRIORITY_HIGH - THREAD_PRIORITY_LOW))); \ + } \ + ) + +/* + * this macro-function takes a number of milliseconds and turns it + * into a valid timeslice according to the scheduler quantum. + * + * for example, with a quantum of 25ms and a given number of 264 milliseconds, + * this macro-function would return 275ms, the upper rounded number. + */ + +#define SCHEDULER_SCALE(_timeslice_) \ + ((((_timeslice_) % SCHEDULER_GRANULARITY) != 0) ? \ + (((_timeslice_) + SCHEDULER_GRANULARITY) - \ + (_timeslice_) % SCHEDULER_GRANULARITY) \ + : (_timeslice_)) + +/* + * this macro-function computes the timeslice given by the kernel to a + * thread based on its character. + * + * the character basically returns task->priority * thread->priority. + * + * this number is then turned into a timeslice i.e within the timeslice + * range [SCHEDULER_TIMESLICE_LOW, SCHEDULER_TIMESLICE_HIGH]. + * + * finally, the timeslice is scaled i.e rounded up in order to fit the + * scheduling unit known as the quantum. + */ + +#define SCHEDULER_TIMESLICE(_thread_) \ + ( \ + { \ + t_priority _character_; \ + t_timeslice _timeslice_; \ + \ + _character_ = SCHEDULER_CHARACTER((_thread_)); \ + \ + _timeslice_ = \ + SCHEDULER_TIMESLICE_LOW + \ + ((_character_ * \ + (SCHEDULER_TIMESLICE_HIGH - SCHEDULER_TIMESLICE_LOW)) / \ + ((TASK_PRIORITY_KERNEL_HIGH - TASK_PRIORITY_BACKGROUND_LOW) * \ + (THREAD_PRIORITY_HIGH - THREAD_PRIORITY_LOW))); \ + \ + SCHEDULER_SCALE(_timeslice_); \ + } \ + ) + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * the scheduler object which managers thread candidates for a + * given CPU specified by _cpu_. + * + * the _thread_ attribute represents the currently scheduled thread + * which operates at the priority _priority_. this thread stil has + * _timeslice_ milliseconds of execution time before a context switch + * occurs. + * + * the _state_ attribute represents the scheduler's current state, either + * started or stopped. + */ + +typedef struct +{ + i_cpu cpu; + + i_thread thread; + t_timeslice timeslice; + t_priority priority; + + /* FIXME[code to complete] */ + + t_state state; + + machine_data(o_scheduler); +} o_scheduler; + +/* + * the scheduler manager's structure which contains the quantum _quantum_ + * i.e the smaller unit of execution time, the idle thread's identifier _idle_ + * and the sets of schedulers, one scheduler per CPU. + */ + +typedef struct +{ + t_quantum quantum; + + i_thread idle; + + i_set schedulers; + + machine_data(m_scheduler); +} m_scheduler; + +/* + * the scheduler dispatcher. + */ + +typedef struct +{ + t_error (*scheduler_show)(i_cpu, + mt_margin); + t_error (*scheduler_dump)(void); + t_error (*scheduler_start)(i_cpu); + t_error (*scheduler_stop)(i_cpu); + t_error (*scheduler_quantum)(t_quantum); + t_error (*scheduler_yield)(void); + t_error (*scheduler_elect)(void); + t_error (*scheduler_add)(i_thread); + t_error (*scheduler_remove)(i_thread); + t_error (*scheduler_update)(i_thread); + t_error (*scheduler_initialize)(void); + t_error (*scheduler_clean)(void); +} d_scheduler; + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../../core/scheduler/scheduler-mfq.c + */ + +/* + * ../../core/scheduler/scheduler-mfq.c + */ + +t_error scheduler_show(i_cpu id, + mt_margin margin); + +t_error scheduler_dump(void); + +t_error scheduler_start(i_cpu id); + +t_error scheduler_stop(i_cpu id); + +t_error scheduler_quantum(t_quantum quantum); + +t_error scheduler_yield(void); + +t_error scheduler_elect(void); + +t_error scheduler_add(i_thread id); + +t_error scheduler_remove(i_thread id); + +t_error scheduler_update(i_thread id); + +t_error scheduler_exist(i_cpu id); + +t_error scheduler_get(i_cpu id, + o_scheduler** object); + +t_error scheduler_current(o_scheduler** scheduler); + +t_error scheduler_initialize(void); + +t_error scheduler_clean(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/core/include/segment.h b/kaneton/core/include/segment.h new file mode 100644 index 0000000..847f026 --- /dev/null +++ b/kaneton/core/include/segment.h @@ -0,0 +1,304 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/include/segment.h + * + * created julien quintard [wed jun 6 14:00:28 2007] + * updated julien quintard [fri apr 8 09:04:47 2011] + */ + +#ifndef CORE_SEGMENT_H +#define CORE_SEGMENT_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include +#include +#include + +#include + +/* + * ---------- algorithms ------------------------------------------------------ + */ + +/* + * the support segment algorithms. + */ + +#define SEGMENT_ALGORITHM_FIT (1 << 0) + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * the options related to the segments. + * + * the 'system' option indicates the segment contains data related to + * the system management such as hardware data structure for instance. + * + * the 'norelocate' option is used by the resize() function. + */ + +#define SEGMENT_OPTION_NONE (1 << 0) +#define SEGMENT_OPTION_SYSTEM (1 << 1) + +#define SEGMENT_OPTION_NORELOCATE (1 << 1) + +/* + * the default size of the bpt nodes. + */ + +#define SEGMENT_BPT_NODESZ 4096 + +/* + * this macro defines the size of the vault i.e the set of segments + * which must be temporarily prevented from being reserved. + */ + +#define SEGMENT_VAULT_SIZE 8 + +/* + * these macros indicate the state of a vault entry. + */ + +#define SEGMENT_VAULT_STATE_AVAILABLE 0 +#define SEGMENT_VAULT_STATE_USED 1 + +/* + * ---------- macro-functions ------------------------------------------------- + */ + +/* + * this macro-function computes a segment identifier based on the address. + */ + +#define SEGMENT_IDENTIFIER(_object_) \ + (i_segment)((_object_)->address / ___kaneton$framesz) + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * this structure represents a memory area which needs to be prevented + * from being reserved. + */ + +typedef struct +{ + t_state state; + t_paddr address; + t_psize size; +} s_segment_zone; + +/* + * the segment object is identified by a unique identifier 'id' and + * belongs to an address space 'as'. + * + * in addition, every segment has a type 'type', a start address 'address' + * and a size 'size' along with a set of permissions 'permissions'. + */ + +typedef struct +{ + i_segment id; + + i_as as; + + t_paddr address; + t_psize size; + + t_permissions permissions; + + t_options options; + + machine_data(o_segment); +} o_segment; + +/* + * the segment manager. + * + * the 'base' and 'size' attributes represent the start and size of + * the segment space i.e the available physical memory. + */ + +typedef struct +{ + t_paddr base; + t_psize size; + + i_set segments; + + s_segment_zone vault[SEGMENT_VAULT_SIZE]; + + machine_data(m_segment); +} m_segment; + +/* + * the segment dispatcher. + */ + +typedef struct +{ + t_error (*segment_show)(i_segment, + mt_margin); + t_error (*segment_dump)(void); + t_error (*segment_protect)(t_paddr, + t_psize); + t_error (*segment_unprotect)(t_paddr, + t_psize); + t_error (*segment_clone)(i_as, + i_segment, + i_segment*); + t_error (*segment_inject)(i_as, + o_segment*, + i_segment*); + t_error (*segment_give)(i_segment, + i_as); + t_error (*segment_resize)(i_segment, + t_psize, + i_segment*); + t_error (*segment_split)(i_segment, + t_psize, + i_segment*, + i_segment*); + t_error (*segment_coalesce)(i_segment, + i_segment, + i_segment*); + t_error (*segment_read)(i_segment, + t_paddr, + void*, + t_psize); + t_error (*segment_write)(i_segment, + t_paddr, + const void*, + t_psize); + t_error (*segment_copy)(i_segment, + t_paddr, + i_segment, + t_paddr, + t_psize); + t_error (*segment_reserve)(i_as, + t_psize, + t_permissions, + i_segment*); + t_error (*segment_release)(i_segment); + t_error (*segment_permissions)(i_segment, + t_permissions); + t_error (*segment_flush)(i_as); + t_error (*segment_initialize)(); + t_error (*segment_clean)(void); +} d_segment; + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../../core/segment/segment-fit.c + */ + +/* + * ../../core/segment/segment-fit.c + */ + +t_error segment_show(i_segment segid, + mt_margin margin); + +t_error segment_dump(void); + +t_error segment_protect(t_paddr address, + t_psize size); + +t_error segment_unprotect(t_paddr address, + t_psize size); + +t_error segment_protected(t_paddr address); + +t_error segment_fit_first(i_as asid, + t_psize size, + t_paddr* address); + +t_error segment_space(i_as asid, + t_psize size, + t_paddr* address); + +t_error segment_clone(i_as asid, + i_segment old, + i_segment* new); + +t_error segment_inject(i_as asid, + o_segment* object, + i_segment* id); + +t_error segment_give(i_segment segid, + i_as asid); + +t_error segment_resize(i_segment old, + t_psize size, + t_options options, + i_segment* new); + +t_error segment_split(i_segment segid, + t_psize size, + i_segment* left, + i_segment* right); + +t_error segment_coalesce(i_segment left, + i_segment right, + i_segment* id); + +t_error segment_read(i_segment segid, + t_paddr offs, + void* buffer, + t_psize sz); + +t_error segment_write(i_segment segid, + t_paddr offs, + const void* buffer, + t_psize sz); + +t_error segment_copy(i_segment dst, + t_paddr offsd, + i_segment src, + t_paddr offss, + t_psize sz); + +t_error segment_reserve(i_as asid, + t_psize size, + t_permissions perms, + t_options options, + i_segment* id); + +t_error segment_release(i_segment segid); + +t_error segment_permissions(i_segment segid, + t_permissions perms); + +t_error segment_flush(i_as asid); + +t_error segment_locate(t_paddr address, + i_segment* id); + +t_error segment_exist(i_segment segid); + +t_error segment_get(i_segment segid, + o_segment** object); + +t_error segment_initialize(t_paddr base, + t_psize size); + +t_error segment_clean(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/core/include/set-array.h b/kaneton/core/include/set-array.h new file mode 100644 index 0000000..92622f4 --- /dev/null +++ b/kaneton/core/include/set-array.h @@ -0,0 +1,49 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/include/set-array.h + * + * created julien quintard [wed jun 6 14:13:43 2007] + * updated julien quintard [sun dec 12 22:44:06 2010] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file supposes it is included by the set.h header file. + */ + +#ifndef CORE_SET_ARRAY_H +#define CORE_SET_ARRAY_H 1 + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * this structure is included in the set descriptor and provides + * specific information for the set implementation. + */ + +typedef struct +{ + t_size initsz; + + void** array; + t_setsz arraysz; +} s_set_array; + +/* + * the implementation-specific iterator. + */ + +typedef struct +{ + t_setsz i; +} s_iterator_array; + +#endif diff --git a/kaneton/core/include/set-bpt.h b/kaneton/core/include/set-bpt.h new file mode 100644 index 0000000..8d3510d --- /dev/null +++ b/kaneton/core/include/set-bpt.h @@ -0,0 +1,106 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/include/set-bpt.h + * + * created julien quintard [wed jun 6 14:14:36 2007] + * updated julien quintard [sun dec 12 22:46:19 2010] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file supposes it is included by the set.h header file. + */ + +#ifndef CORE_SET_BPT_H +#define CORE_SET_BPT_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#undef BPT_DEBUG + +#include + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * these macro defines the set implementation bpt types. + */ + +#define SET_BPT_ADDR_T void* +#define SET_BPT_KEY_T t_id +#define SET_BPT_VALUE_T void* + +#define SET_BPT_UADDR NULL +#define SET_BPT_UKEY ID_UNUSED +#define SET_BPT_UVALUE NULL + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * bpt internal and leaf entries. + */ + +typedef struct +{ + SET_BPT_KEY_T id; + SET_BPT_ADDR_T data; +} s_set_bpt_inentry; + +typedef struct +{ + SET_BPT_KEY_T id; + SET_BPT_VALUE_T data; +} s_set_bpt_lfentry; + +/* + * this macro-function call generates the bpt types. + */ + +bpt_make_types(set, BPT_NODESZ_T, BPT_NDI_T, BPT_UNI_T, BPT_NODES_T, + BPT_HEIGHT_T, SET_BPT_ADDR_T, SET_BPT_KEY_T, SET_BPT_VALUE_T, + s_set_bpt_inentry, s_set_bpt_lfentry) + +/* + * set descriptor's bpt-specific types. + */ + +typedef struct +{ + t_bpt_unused(set) unused; + t_bpt_uni(set) unusedsz; + + t_bpt(set) bpt; +} s_set_bpt; + +/* + * the bpt-specific iterator. + */ + +typedef struct +{ + t_bpt_entry(set) entry; +} s_iterator_bpt; + +/* + * ---------- prototypes ------------------------------------------------------ + */ + +/* + * this macro-function call generates the bpt prototypes. + */ + +bpt_make_protos(set) + +#endif diff --git a/kaneton/core/include/set-ll.h b/kaneton/core/include/set-ll.h new file mode 100644 index 0000000..5e036ef --- /dev/null +++ b/kaneton/core/include/set-ll.h @@ -0,0 +1,58 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/include/set-ll.h + * + * created julien quintard [wed jun 6 14:25:53 2007] + * updated julien quintard [sun dec 12 22:44:29 2010] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file supposes it is include by set.h + */ + +#ifndef CORE_SET_LL_H +#define CORE_SET_LL_H 1 + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * a node of the linked-list. + */ + +typedef struct s_set_ll_node +{ + void* data; + + struct s_set_ll_node* previous; + struct s_set_ll_node* next; +} s_set_ll_node; + +/* + * set descriptor's linked-list-specific data. + */ + +typedef struct +{ + s_set_ll_node* head; + s_set_ll_node* tail; +} s_set_ll; + +/* + * the linked-list iterator structure. + */ + +typedef struct +{ + s_set_ll_node* node; +} s_iterator_ll; + +#endif diff --git a/kaneton/core/include/set.h b/kaneton/core/include/set.h new file mode 100644 index 0000000..ed891c7 --- /dev/null +++ b/kaneton/core/include/set.h @@ -0,0 +1,753 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/include/set.h + * + * created julien quintard [wed jun 6 11:56:46 2007] + * updated julien quintard [wed dec 15 09:55:26 2010] + */ + +#ifndef CORE_SET_H +#define CORE_SET_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include +#include +#include + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * the size of a set i.e the number of objects a set contains. + */ + +typedef t_sint64 t_setsz; + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include +#include +#include + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * iterator's state. + */ + +#define ITERATOR_STATE_USED 0x01 +#define ITERATOR_STATE_UNUSED 0x02 + +/* + * types i.e set implementations. + */ + +#define SET_TYPE_ARRAY 0x01 +#define SET_TYPE_BPT 0x02 +#define SET_TYPE_LL 0x03 +#define SET_TYPE_PIPE 0x04 +#define SET_TYPE_STACK 0x05 + +/* + * options. + */ + +#define SET_OPTION_NONE (0 << 0) +#define SET_OPTION_FORWARD (1 << 0) +#define SET_OPTION_BACKWARD (1 << 1) +#define SET_OPTION_CONTAINER (1 << 2) +#define SET_OPTION_ALLOCATE (1 << 3) +#define SET_OPTION_FREE (1 << 4) +#define SET_OPTION_SORT (1 << 5) +#define SET_OPTION_ORGANISE (1 << 6) + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * the iterator type. + */ + +typedef struct +{ + union + { + s_iterator_array array; + s_iterator_bpt bpt; + s_iterator_ll ll; + } u; +} s_iterator; + +/* + * the set object. + * + * the 'id' field identifies uniquely the set. the number of elements + * contained in a set is given by 'size'. likewise, 'datasz' records the + * size of the objects being stored in this set. + * + * the 'type' attribute provides the set implementation and is used + * by set_trap() to forward the call the the apporpriate set implementation: + * bpt, ll, array etc. the 'options' attribute contains the options associated + * with the set. + * + * finally the union 'u' includes some set-implementation-specific data. + */ + +typedef struct +{ + i_set id; + + t_setsz size; + + t_type type; + t_options options; + + t_size datasz; + + union + { + s_set_array array; + s_set_bpt bpt; + s_set_ll ll; + } u; +} o_set; + +/* + * the set manager. + * + * the 'id' attribute is an identifier object which is used for generating + * set identifiers. + * + * the 'sets' attribute contains the set identifier of the container i.e + * the set which contains the other sets. indeed, every set is also stored + * in a set since sets are objects. the 'container' attribute represents + * the so-called container which holds all the set objects. + */ + +typedef struct +{ + o_id id; + + i_set sets; + + o_set* container; +} m_set; + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * these macro-functions are wrapper which forward the call to the + * appopriate set implementation, taking care to provide all the + * arguments. + * + * since the number of arguments is not constrained by these macro-functions + * a set implementation can accept two arguments for a function while + * another implementation could accept nine. obviously this means that + * changing the set type implies updating the source code to adapt + * such calls but in practice there are not many differences between + * the implementations as they all comply with the generic set interface. + * + * note the specific use of the ## in this macro-function. using the ## + * with variadic macros enables the use of a variable number of arguments, + * this number being twelve, three, or even zero. + * + * indeed, let us consider the following example + * + * #define printf(_format_, _args_...) \ + * module_call(console, print, _format_, _args_) + * + * printf("kaneton\n"); + * + * this macro function could not work with an empty set or arguments + * _args_ because the c-preprocess would expand the macro-function to: + * + * module_call(console, print, "kaneton\n", ); + * + * one can notice that the comma following the format string remains. using + * the GNU pre-processor specific ##, the comma is removed should the + * following variadic argument be empty. + */ + +#define set_trap(_func_, _id_, _args_...) \ + ( \ + { \ + t_error _r_ = ERROR_KO; \ + o_set* _set_; \ + \ + if (set_descriptor((_id_), &_set_) == ERROR_OK) \ + { \ + switch (_set_->type) \ + { \ + case SET_TYPE_ARRAY: \ + _r_ = _func_##_array((_id_), ##_args_); \ + break; \ + case SET_TYPE_BPT: \ + _r_ = _func_##_bpt((_id_), ##_args_); \ + break; \ + case SET_TYPE_LL: \ + _r_ = _func_##_ll((_id_), ##_args_); \ + break; \ + case SET_TYPE_PIPE: \ + _r_ = _func_##_pipe((_id_), ##_args_); \ + break; \ + case SET_TYPE_STACK: \ + _r_ = _func_##_stack((_id_), ##_args_); \ + break; \ + } \ + } \ + \ + _r_; \ + } \ + ) + +#define set_reserve(_type_, _args_...) \ + set_reserve_##_type_(_args_) + +#define set_exist(_id_, _args_...) \ + set_trap(set_exist, _id_, ##_args_) + +#define set_release(_id_, _args_...) \ + set_trap(set_release, _id_, ##_args_) + +#define set_head(_id_, _args_...) \ + set_trap(set_head, _id_, ##_args_) + +#define set_tail(_id_, _args_...) \ + set_trap(set_tail, _id_, ##_args_) + +#define set_previous(_id_, _args_...) \ + set_trap(set_previous, _id_, ##_args_) + +#define set_next(_id_, _args_...) \ + set_trap(set_next, _id_, ##_args_) + +#define set_add(_id_, _args_...) \ + set_trap(set_add, _id_, ##_args_) + +#define set_remove(_id_, _args_...) \ + set_trap(set_remove, _id_, ##_args_) + +#define set_delete(_id_, _args_...) \ + set_trap(set_delete, _id_, ##_args_) + +#define set_flush(_id_, _args_...) \ + set_trap(set_flush, _id_, ##_args_) + +#define set_insert(_id_, _args_...) \ + set_trap(set_insert, _id_, ##_args_) + +#define set_append(_id_, _args_...) \ + set_trap(set_append, _id_, ##_args_) + +#define set_before(_id_, _args_...) \ + set_trap(set_before, _id_, ##_args_) + +#define set_after(_id_, _args_...) \ + set_trap(set_after, _id_, ##_args_) + +#define set_locate(_id_, _args_...) \ + set_trap(set_locate, _id_, ##_args_) + +#define set_object(_id_, _args_...) \ + set_trap(set_object, _id_, ##_args_) + +#define set_show(_id_, _args_...) \ + set_trap(set_show, _id_, ##_args_) + +#define set_push(_id_, _args_...) \ + set_trap(set_push, _id_, ##_args_) + +#define set_pop(_id_, _args_...) \ + set_trap(set_pop, _id_, ##_args_) + +#define set_pick(_id_, _args_...) \ + set_trap(set_pick, _id_, ##_args_) + +/* + * the foreach() macro-function enables the programmer to easily + * walk through a set. + * + * for instance, the following explore a set in the forward way: + * + * i_set myset; + * s_iterator myiterator; + * t_state mystate; + * + * [...] + * + * set_foreach(SET_OPTION_FORWARD, myset, myiterator, mystate) + * { + * [...] + * } + */ + +#define set_foreach(_opt_, _id_, _iterator_, _state_) \ + for ((_state_) = ITERATOR_STATE_UNUSED; \ + (((_state_) == ITERATOR_STATE_UNUSED) ? \ + ((_opt_) == SET_OPTION_FORWARD ? \ + set_head((_id_), (_iterator_)) == ERROR_TRUE : \ + set_tail((_id_), (_iterator_)) == ERROR_TRUE) : \ + ((_opt_) == SET_OPTION_FORWARD ? \ + set_next((_id_), *(_iterator_), (_iterator_)) == \ + ERROR_TRUE : \ + set_previous((_id_), *(_iterator_), (_iterator_)) == \ + ERROR_TRUE)); \ + (_state_) = ITERATOR_STATE_USED \ + ) + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../../core/set/set.c + * ../../core/set/set-array.c + * ../../core/set/set-ll.c + * ../../core/set/set-bpt.c + * ../../core/set/set-pipe.c + * ../../core/set/set-stack.c + */ + +/* + * ../../core/set/set.c + */ + +t_error set_dump(void); + +t_error set_empty(i_set setid); + +t_error set_size(i_set setid, + t_setsz* size); + +t_error set_new(o_set* object); + +t_error set_destroy(i_set setid); + +t_error set_descriptor(i_set setid, + o_set** object); + +t_error set_get(i_set setid, + t_id id, + void** object); + +t_error set_initialize(void); + +t_error set_clean(void); + + +/* + * ../../core/set/set-array.c + */ + +t_error set_exist_array(i_set setid, + t_id id); + +t_error set_show_array(i_set setid, + mt_margin margin); + +t_error set_head_array(i_set setid, + s_iterator* iterator); + +t_error set_tail_array(i_set setid, + s_iterator* iterator); + +t_error set_previous_array(i_set setid, + s_iterator current, + s_iterator* previous); + +t_error set_next_array(i_set setid, + s_iterator current, + s_iterator* next); + +t_error set_expand_array(o_set *object, + void *data); + +t_error set_insert_array_at(o_set *object, + t_setsz pos, + void *data); + +t_error set_insert_array(i_set setid, + void* data); + +t_error set_append_array(i_set setid, + void* data); + +t_error set_before_array(i_set setid, + s_iterator iterator, + void* data); + +t_error set_after_array(i_set setid, + s_iterator iterator, + void* data); + +t_error set_add_array(i_set setid, + void* data); + +t_error set_remove_array(i_set setid, + t_id id); + +t_error set_delete_array(i_set setid, + s_iterator iterator); + +t_error set_flush_array(i_set setid); + +t_error set_locate_array(i_set setid, + t_id id, + s_iterator* iterator); + +t_error set_object_array(i_set setid, + s_iterator iterator, + void** data); + +t_error set_reserve_array(t_options options, + t_setsz initsz, + t_size datasz, + i_set* id); + +t_error set_release_array(i_set setid); + +t_error set_push_array(i_set setid, + void* data); + +t_error set_pop_array(i_set setid); + +t_error set_pick_array(i_set setid, + void** data); + + +/* + * ../../core/set/set-ll.c + */ + +t_error set_exist_ll(i_set setid, + t_id id); + +t_error set_show_ll(i_set setid, + mt_margin margin); + +t_error set_head_ll(i_set setid, + s_iterator* iterator); + +t_error set_tail_ll(i_set setid, + s_iterator* iterator); + +t_error set_previous_ll(i_set setid, + s_iterator current, + s_iterator* previous); + +t_error set_next_ll(i_set setid, + s_iterator current, + s_iterator* next); + +t_error set_insert_ll(i_set setid, + void* data); + +t_error set_append_ll(i_set setid, + void* data); + +t_error set_before_ll(i_set setid, + s_iterator iterator, + void* data); + +t_error set_after_ll(i_set setid, + s_iterator iterator, + void* data); + +t_error set_add_ll(i_set setid, + void* data); + +t_error set_remove_ll(i_set setid, + t_id id); + +t_error set_delete_ll(i_set setid, + s_iterator iterator); + +t_error set_flush_ll(i_set setid); + +t_error set_locate_ll(i_set setid, + t_id id, + s_iterator* iterator); + +t_error set_object_ll(i_set setid, + s_iterator iterator, + void** data); + +t_error set_reserve_ll(t_options options, + t_size datasz, + i_set* id); + +t_error set_release_ll(i_set setid); + +t_error set_push_ll(i_set setid, + void* data); + +t_error set_pop_ll(i_set setid); + +t_error set_pick_ll(i_set setid, + void** data); + + +/* + * ../../core/set/set-bpt.c + */ + +void set_load_bpt(t_bpt(set)* bpt, + t_bpt_imm(set)* node, + t_bpt_node(set) addr); + +void set_unload_bpt(t_bpt(set)* bpt, + t_bpt_imm(set)* node); + +int set_addrcmp_bpt(t_bpt(set)* bpt, + t_bpt_addr(set) addr1, + t_bpt_addr(set) addr2); + +int set_keycmp_bpt(t_bpt(set)* bpt, + t_bpt_key(set) key1, + t_bpt_key(set) key2); + +int set_valcmp_bpt(t_bpt(set)* bpt, + t_bpt_value(set) value1, + t_bpt_value(set) value2); + +t_error set_exist_bpt(i_set setid, + t_id id); + +t_error set_build_bpt(o_set* object, + BPT_NODESZ_T nodesz); + +t_error set_adjust_bpt(o_set* object, + t_bpt_uni(set) alloc, + t_bpt_uni(set) size); + +t_error set_destroy_bpt(o_set* object); + +t_error set_show_bpt(i_set setid, + mt_margin margin); + +t_error set_head_bpt(i_set setid, + s_iterator* iterator); + +t_error set_tail_bpt(i_set setid, + s_iterator* iterator); + +t_error set_previous_bpt(i_set setid, + s_iterator current, + s_iterator* previous); + +t_error set_next_bpt(i_set setid, + s_iterator current, + s_iterator* next); + +t_error set_insert_bpt(i_set setid, + void* data); + +t_error set_append_bpt(i_set setid, + void* data); + +t_error set_before_bpt(i_set setid, + s_iterator iterator, + void* data); + +t_error set_after_bpt(i_set setid, + s_iterator iterator, + void* data); + +t_error set_add_bpt(i_set setid, + void* data); + +t_error set_remove_bpt(i_set setid, + t_id id); + +t_error set_delete_bpt(i_set setid, + s_iterator iterator); + +t_error set_flush_bpt(i_set setid); + +t_error set_locate_bpt(i_set setid, + t_id id, + s_iterator* iterator); + +t_error set_object_bpt(i_set setid, + s_iterator iterator, + void** data); + +t_error set_reserve_bpt(t_options options, + t_size datasz, + t_bpt_nodesz(set) nodesz, + i_set* id); + +t_error set_release_bpt(i_set setid); + +t_error set_push_bpt(i_set setid, + void* data); + +t_error set_pop_bpt(i_set setid); + +t_error set_pick_bpt(i_set setid, + void** data); + + +/* + * ../../core/set/set-pipe.c + */ + +t_error set_reserve_pipe(t_options options, + t_size datasz, + i_set* id); + +t_error set_exist_pipe(i_set setid, + t_id id); + +t_error set_show_pipe(i_set setid, + mt_margin margin); + +t_error set_release_pipe(i_set setid); + +t_error set_flush_pipe(i_set setid); + +t_error set_push_pipe(i_set setid, + void* data); + +t_error set_pick_pipe(i_set setid, + void** data); + +t_error set_pop_pipe(i_set setid); + +t_error set_head_pipe(i_set setid, + s_iterator* iterator); + +t_error set_tail_pipe(i_set setid, + s_iterator* iterator); + +t_error set_previous_pipe(i_set setid, + s_iterator current, + s_iterator* previous); + +t_error set_next_pipe(i_set setid, + s_iterator current, + s_iterator* next); + +t_error set_insert_pipe(i_set setid, + void* data); + +t_error set_append_pipe(i_set setid, + void* data); + +t_error set_before_pipe(i_set setid, + s_iterator iterator, + void* data); + +t_error set_after_pipe(i_set setid, + s_iterator iterator, + void* data); + +t_error set_add_pipe(i_set setid, + void* data); + +t_error set_remove_pipe(i_set setid, + t_id id); + +t_error set_delete_pipe(i_set setid, + s_iterator iterator); + +t_error set_locate_pipe(i_set setid, + t_id id, + s_iterator* iterator); + +t_error set_object_pipe(i_set setid, + s_iterator iterator, + void** data); + + +/* + * ../../core/set/set-stack.c + */ + +t_error set_reserve_stack(t_options options, + t_size datasz, + i_set* id); + +t_error set_exist_stack(i_set setid, + t_id id); + +t_error set_show_stack(i_set setid, + mt_margin margin); + +t_error set_release_stack(i_set setid); + +t_error set_flush_stack(i_set setid); + +t_error set_push_stack(i_set setid, + void* data); + +t_error set_pick_stack(i_set setid, + void** data); + +t_error set_pop_stack(i_set setid); + +t_error set_head_stack(i_set setid, + s_iterator* iterator); + +t_error set_tail_stack(i_set setid, + s_iterator* iterator); + +t_error set_previous_stack(i_set setid, + s_iterator current, + s_iterator* previous); + +t_error set_next_stack(i_set setid, + s_iterator current, + s_iterator* next); + +t_error set_insert_stack(i_set setid, + void* data); + +t_error set_append_stack(i_set setid, + void* data); + +t_error set_before_stack(i_set setid, + s_iterator iterator, + void* data); + +t_error set_after_stack(i_set setid, + s_iterator iterator, + void* data); + +t_error set_add_stack(i_set setid, + void* data); + +t_error set_remove_stack(i_set setid, + t_id id); + +t_error set_delete_stack(i_set setid, + s_iterator iterator); + +t_error set_locate_stack(i_set setid, + t_id id, + s_iterator* iterator); + +t_error set_object_stack(i_set setid, + s_iterator iterator, + void** data); + + +/* + * eop + */ + +#endif diff --git a/kaneton/core/include/task.h b/kaneton/core/include/task.h new file mode 100644 index 0000000..0943f9f --- /dev/null +++ b/kaneton/core/include/task.h @@ -0,0 +1,305 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/include/task.h + * + * created julien quintard [wed jun 6 14:27:31 2007] + * updated julien quintard [sat dec 18 11:07:11 2010] + */ + +#ifndef CORE_TASK_H +#define CORE_TASK_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include +#include +#include +#include + +#include + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * the classes of tasks. the class has an impact on the range of priorities + * for the task hence on the scheduling priority. + * + * besides, the class indicates the task's privileges on the system. indeed + * while the kernel task can do everything, a driver can access hardware + * devices. a service is a task which provide a so-called service i.e waits + * for messages and performs an operation on behalf of the caller. finally + * a guest task in an application which does nothing but to compute and + * request other tasks to perform operations on its behalf. + */ + +#define TASK_CLASS_KERNEL (1 << 0) +#define TASK_CLASS_DRIVER (1 << 1) +#define TASK_CLASS_SERVICE (1 << 2) +#define TASK_CLASS_GUEST (1 << 3) + +/* + * the behaviour specifies the way the task behaves depending on the + * program it contains. the kernel behaviour is reserved for the kernel + * task. a real-time task is often I/O bound and therefore needs to be + * scheduled quickly once woken up. an interactive task is a normal task + * which needs a little more of reactivity since interacting with the + * user through a user interface. the timesharing behaviour is the default + * for tasks performing normal computations. finally, the background + * behaviour can be used by tasks running in background, blocked most of + * the time waiting for time to pass for instance. + * + * note that the behaviour obviously has an impact on the scheduling + * election algorithm. + */ + +#define TASK_BEHAVIOUR_KERNEL (1 << 0) +#define TASK_BEHAVIOUR_REALTIME (1 << 1) +#define TASK_BEHAVIOUR_INTERACTIVE (1 << 2) +#define TASK_BEHAVIOUR_TIMESHARING (1 << 3) +#define TASK_BEHAVIOUR_BACKGROUND (1 << 4) + +/* + * the priority ranges below are specific to the task's class. therefore + * a task's priority can only evolve in this range. + */ + +#define TASK_PRIORITY_KERNEL 230 +#define TASK_PRIORITY_KERNEL_HIGH 250 +#define TASK_PRIORITY_KERNEL_LOW 210 + +#define TASK_PRIORITY_REALTIME 190 +#define TASK_PRIORITY_REALTIME_HIGH 210 +#define TASK_PRIORITY_REALTIME_LOW 170 + +#define TASK_PRIORITY_INTERACTIVE 150 +#define TASK_PRIORITY_INTERACTIVE_HIGH 170 +#define TASK_PRIORITY_INTERACTIVE_LOW 130 + +#define TASK_PRIORITY_TIMESHARING 90 +#define TASK_PRIORITY_TIMESHARING_HIGH 130 +#define TASK_PRIORITY_TIMESHARING_LOW 50 + +#define TASK_PRIORITY_BACKGROUND 30 +#define TASK_PRIORITY_BACKGROUND_HIGH 50 +#define TASK_PRIORITY_BACKGROUND_LOW 10 + +/* + * the initial sizes for the sets of threads etc. + */ + +#define TASK_THREADS_INITSZ 0x1 +#define TASK_WAITS_INITSZ 0x1 + +/* + * the current task's state. + * + * note that whenever a task exists, its state is set to ZOMBIE. a task + * stays in this state until a thread probes its state through the task_wait() + * functionality. at this point, the task's state is set to dead until its + * resources are completely cleaned from the system. + */ + +#define TASK_STATE_START 1 +#define TASK_STATE_STOP 2 +#define TASK_STATE_BLOCK 3 +#define TASK_STATE_ZOMBIE 4 +#define TASK_STATE_DEAD 5 + +/* + * this macro defines the delay, in milliseconds, for the morgue to + * be triggered and clean the dead tasks. + */ + +#define TASK_MORGUE_DELAY 10000 + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * the task object's structure. + * + * the 'cpu' indicates the CPU on which the task will be scheduled. + * + * the 'parent' attribute specifies the identifier of the task which + * creted this task while 'children' references the set of children tasks. + * + * the 'class', 'behaviour' and 'priority' specific the task's characteristics. + * for more information, please refer to the macro related to these + * attributes. + * + * the 'as' field contains the identifier of the task's address space. + * + * the 'threads' specifies the set containing the identifiers of the threads + * living in this task. + * + * the 'state' indicates the current task's state: started, stopped, blocked + * etc. + * + * the 'waits' set contains the identifiers of the threads which are waiting + * for the task to change its state to a particular one. + * + * the 'value' attribute contains the exit code provided through the + * task_exit() call. + * + * finally, the 'message' contains the set of messages associated with + * the task. + * + * note that a timer is provided should the task need to trigger an + * action. for instance the timer is used for the task to sleep for some + * time. + */ + +typedef struct +{ + i_task id; + + i_cpu cpu; + + i_task parent; + i_set children; + + t_class class; + t_behaviour behaviour; + t_priority priority; + + i_as as; + i_set threads; + + t_state state; + + i_set waits; + + t_value value; + + i_set messages; + + i_timer timer; + + machine_data(o_task); +} o_task; + +/* + * the task manager's structure. + */ + +typedef struct +{ + o_id id; + + i_set tasks; + + struct + { + i_set field; + i_timer timer; + } morgue; + + machine_data(m_task); +} m_task; + +/* + * the task dispatcher. + */ + +typedef struct +{ + t_error (*task_show)(i_task, + mt_margin); + t_error (*task_dump)(void); + t_error (*task_reserve)(t_class, + t_behaviour, + t_priority, + i_task*); + t_error (*task_release)(i_task); + t_error (*task_priority)(i_task, + t_priority); + t_error (*task_start)(i_task); + t_error (*task_stop)(i_task); + t_error (*task_block)(i_task); + t_error (*task_exit)(i_task, + t_value); + t_error (*task_wait)(i_thread, + i_task, + t_state, + s_wait*); + t_error (*task_sleep)(i_task, + t_delay); + t_error (*task_initialize)(void); + t_error (*task_clean)(void); +} d_task; + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../../core/task/task.c + */ + +/* + * ../../core/task/task.c + */ + +t_error task_show(i_task id, + mt_margin margin); + +t_error task_dump(void); + +t_error task_reserve(t_class class, + t_behaviour behav, + t_priority prior, + i_task* id); + +t_error task_release(i_task id); + +t_error task_priority(i_task id, + t_priority prior); + +t_error task_start(i_task id); + +t_error task_stop(i_task id); + +t_error task_block(i_task id); + +t_error task_exit(i_task id, + t_value value); + +void task_bury(i_timer timer, + t_vaddr data); + +t_error task_wait(i_thread id, + i_task target, + t_state state, + s_wait* wait); + +void task_wakeup(i_timer timer, + t_vaddr data); + +t_error task_sleep(i_task id, + t_delay milliseconds); + +t_error task_current(i_task* task); + +t_error task_exist(i_task id); + +t_error task_get(i_task id, + o_task** object); + +t_error task_initialize(void); + +t_error task_clean(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/core/include/thread.h b/kaneton/core/include/thread.h new file mode 100644 index 0000000..6aab852 --- /dev/null +++ b/kaneton/core/include/thread.h @@ -0,0 +1,335 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/include/thread.h + * + * created julien quintard [wed jun 6 14:31:49 2007] + * updated julien quintard [sun jan 30 20:56:52 2011] + */ + +#ifndef CORE_THREAD_H +#define CORE_THREAD_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include +#include +#include +#include + +#include + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * the thread priorities. + */ + +#define THREAD_PRIORITY 130 +#define THREAD_PRIORITY_HIGH 250 +#define THREAD_PRIORITY_LOW 10 + +/* + * these macro can be used whenever a stack must be allocated. + */ + +#define THREAD_STACK_ADDRESS_NONE (t_vaddr)NULL + +/* + * the stack sizes. + */ + +#define THREAD_STACK_SIZE 500 * ___kaneton$pagesz +#define THREAD_STACK_SIZE_HIGH 1000 * ___kaneton$pagesz +#define THREAD_STACK_SIZE_LOW 1 * ___kaneton$pagesz + +/* + * some initial size for the sets. + */ + +#define THREAD_WAITS_INITSZ 0x1 + +/* + * the thread states. + * + * note that whenever a thread exits, its state changes to zombie. then, + * once someone has taken notice of the thread's death, the state changes + * to dead. + * + * a dead thread resides in the morgue until it is definitely released. + */ + +#define THREAD_STATE_START 1 +#define THREAD_STATE_STOP 2 +#define THREAD_STATE_BLOCK 3 +#define THREAD_STATE_ZOMBIE 4 +#define THREAD_STATE_DEAD 5 + +/* + * the delay in milliseconds until the morgue runs and releases the + * resources associated to the dead threads. + * + * note that this delay is based on the task's because, whenever a task + * exits, the threads are asked to exit as well. however, the threads + * must be buried before the task. otherwise, at the thread burial, the + * thread's task would be retrieved, but since the task has already been + * buried, an error would occur. + */ + +#define THREAD_MORGUE_DELAY TASK_MORGUE_DELAY / 2 + +/* + * this macro defines the index of the queue in which we wishes the + * idle thread to be placed i.e next to the lowest priority queue. + */ + +#define THREAD_IDLE_QUEUE SCHEDULER_PRIORITY_LOW + 1 + +/* + * this macro defines the idle thread's priority according to the queue + * in which we want it to be placed. + */ + +#define THREAD_IDLE_PRIORITY \ + ( \ + { \ + extern m_kernel _kernel; \ + \ + o_task* _task_; \ + \ + assert(task_get(_kernel.task, &_task_) == ERROR_OK); \ + \ + ((THREAD_IDLE_QUEUE - SCHEDULER_PRIORITY_LOW) * \ + ((TASK_PRIORITY_KERNEL_HIGH - TASK_PRIORITY_BACKGROUND_LOW) * \ + (THREAD_PRIORITY_HIGH - THREAD_PRIORITY_LOW)) / \ + (SCHEDULER_PRIORITY_HIGH - SCHEDULER_PRIORITY_LOW) / \ + (_task_->priority - TASK_PRIORITY_BACKGROUND_LOW)) + \ + THREAD_PRIORITY_LOW + 1; \ + } \ + ) + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * the thread execution context which is composed of the PC - Program + * Counter and the SP - Stack Pointer. + * + * note that, obviously, this is a generic context. a more specific + * context with advanced registers etc. will probably be provided by the + * machine. + */ + +typedef struct +{ + t_vaddr pc; + t_vaddr sp; +} s_thread_context; + +/* + * the thread object identifier by a unique identifier 'id'. + * + * the 'task' references the task the thread belongs to. + * + * the 'priority' indicates the thread priority while the 'state' specifies + * the current thread's state. + * + * the 'waits' set contains the identifiers of the threads waiting for + * the thread to change its state. + * + * the 'wait' structure is used by the thread/task waited for for passing + * information such as the exit value etc. + * + * the 'value' contains the thread's exit value, when it exits! + * + * finally the 'stack' attribute references the thread's stack. + * + * note that a timer is provided should the thread need to trigger an + * action. for instance the timer is used for the thread to sleep for some + * time. + */ + +typedef struct +{ + i_thread id; + + i_task task; + + t_priority priority; + + t_state state; + + i_set waits; + + struct + { + t_state state; + + t_state cause; + t_value value; + } wait; + + t_value value; + + struct + { + t_vaddr base; + t_vsize size; + } stack; + + t_vaddr entry; + + i_timer timer; + + machine_data(o_thread); +} o_thread; + +/* + * the thread manager which is basically composed of the set of threads + * populating the system. + */ + +typedef struct +{ + o_id id; + + i_set threads; + + struct + { + i_set field; + i_timer timer; + } morgue; + + machine_data(m_thread); +} m_thread; + +/* + * the thread dispatcher. + */ + +typedef struct +{ + t_error (*thread_show)(i_thread, + mt_margin); + t_error (*thread_dump)(void); + t_error (*thread_reserve)(i_task, + t_priority, + t_vaddr, + t_vsize, + t_vaddr, + i_thread*); + t_error (*thread_release)(i_thread); + t_error (*thread_priority)(i_thread, + t_priority); + t_error (*thread_start)(i_thread); + t_error (*thread_stop)(i_thread); + t_error (*thread_block)(i_thread); + t_error (*thread_exit)(i_thread, + t_value); + t_error (*thread_wait)(i_thread, + i_thread, + t_state, + s_wait*); + t_error (*thread_arguments)(i_thread, + void*, + t_size); + t_error (*thread_sleep)(i_thread, + t_delay); + t_error (*thread_flush)(i_task); + t_error (*thread_load)(i_thread, + s_thread_context); + t_error (*thread_store)(i_thread, + s_thread_context*); + t_error (*thread_initialize)(void); + t_error (*thread_clean)(void); +} d_thread; + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../../core/thread/thread.c + */ + +/* + * ../../core/thread/thread.c + */ + +t_error thread_show(i_thread threadid, + mt_margin margin); + +t_error thread_dump(void); + +t_error thread_reserve(i_task taskid, + t_priority prior, + t_vaddr stack, + t_vsize stacksz, + t_vaddr entry, + i_thread* id); + +t_error thread_release(i_thread threadid); + +t_error thread_priority(i_thread threadid, + t_priority prior); + +t_error thread_start(i_thread id); + +t_error thread_stop(i_thread id); + +t_error thread_block(i_thread id); + +t_error thread_exit(i_thread id, + t_value value); + +void thread_bury(i_timer timer, + t_vaddr data); + +t_error thread_wait(i_thread id, + i_thread target, + t_state state, + s_wait* wait); + +t_error thread_arguments(i_thread threadid, + void* arguments, + t_vsize size); + +void thread_wakeup(i_timer timer, + t_vaddr data); + +t_error thread_sleep(i_thread id, + t_delay milliseconds); + +t_error thread_flush(i_task taskid); + +t_error thread_load(i_thread threadid, + s_thread_context context); + +t_error thread_store(i_thread threadid, + s_thread_context* context); + +t_error thread_current(i_thread* thread); + +t_error thread_exist(i_thread threadid); + +t_error thread_get(i_thread id, + o_thread** object); + +t_error thread_initialize(void); + +t_error thread_clean(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/core/include/timer.h b/kaneton/core/include/timer.h new file mode 100644 index 0000000..6ccbe4e --- /dev/null +++ b/kaneton/core/include/timer.h @@ -0,0 +1,232 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/include/timer.h + * + * created julien quintard [wed jun 6 15:42:26 2007] + * updated julien quintard [wed mar 2 20:27:39 2011] + */ + +#ifndef CORE_TIMER_H +#define CORE_TIMER_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include +#include +#include + +#include + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * the different types of timers: either a function is called within the + * kernel or a message is sent to a task. + */ + +#define TIMER_TYPE_FUNCTION 0 +#define TIMER_TYPE_MESSAGE 1 + +/* + * the timer options. + * + * the 'repeat' option makes the timer go repeatdly. + */ + +#define TIMER_OPTION_NONE 0 +#define TIMER_OPTION_REPEAT 1 + +/* + * the number of millisecond between each tick: the timers are checked + * after this delay. in other words this is the base unit for times but + * also for the scheduler. + */ + +#define TIMER_DELAY 25 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function casts a function pointer into a proper timer + * handler. + */ + +#define TIMER_ROUTINE(_routine_) \ + ((u_timer_handler)(t_timer_routine)(_routine_)) + +/* + * this macro-function casts a task identifier into a proper timer + * handler. + */ + +#define TIMER_TASK(_task_) \ + ((u_timer_handler)(i_task)(_task_)) + +/* + * this macro-function casts an arbitrary data into a valid timer data. + */ + +#define TIMER_DATA(_data_) \ + (t_data)((_data_)) + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * this type represents a routine-specific timer handler. + */ + +typedef t_error (*t_timer_routine)(i_timer, + t_data); + +/* + * this type is the generic timer handler. + */ + +typedef union +{ + t_timer_routine routine; + i_task task; +} u_timer_handler; + +/* + * the timer object which is identifier by 'id'. + * + * the 'delay' indicates after how many milliseconds the associated + * 'handler' of type 'type' must be triggered. note that some arbitrary + * data 'data' are passed to the handler. these data are provided by + * the user when the timer is reserved. + */ + +typedef struct +{ + i_timer id; + + t_type type; + + t_delay delay; + + t_options options; + t_delay repeat; + + u_timer_handler handler; + t_data data; + + machine_data(o_timer); +} o_timer; + +/* + * the message object for message-based events. + */ + +typedef struct +{ + i_timer id; + + t_data data; +} o_timer_message; + +/* + * the timer manager. + * + * the 'reference' is the number of milliseconds elapsed since the + * timer manager initialisation. each tick triggers the timer manager's + * handler which increases the reference. then, for every expired timer, + * its associated handler is triggered. + */ + +typedef struct +{ + o_id id; + + i_set timers; + + machine_data(m_timer); +} m_timer; + +/* + * the timer dispatcher. + */ + +typedef struct +{ + t_error (*timer_show)(i_timer, + mt_margin); + t_error (*timer_dump)(void); + t_error (*timer_notify)(i_timer); + t_error (*timer_reserve)(t_type, + u_timer_handler, + t_data, + t_delay, + t_options, + i_timer*); + t_error (*timer_release)(i_timer); + t_error (*timer_update)(i_timer, + t_delay); + t_error (*timer_initialize)(void); + t_error (*timer_clean)(void); +} d_timer; + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../../core/timer/timer.c + */ + +/* + * ../../core/timer/timer.c + */ + +void timer_handler(i_event event, + t_data data); + +t_error timer_check(void); + +t_error timer_show(i_timer id, + mt_margin margin); + +t_error timer_dump(void); + +t_error timer_notify(i_timer id); + +t_error timer_reserve(t_type type, + u_timer_handler handler, + t_data data, + t_delay delay, + t_options options, + i_timer* id); + +t_error timer_release(i_timer id); + +t_error timer_update(i_timer id, + t_delay delay); + +t_error timer_flush(void); + +t_error timer_exist(i_timer id); + +t_error timer_get(i_timer id, + o_timer** object); + +t_error timer_initialize(void); + +t_error timer_clean(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/core/include/types.h b/kaneton/core/include/types.h new file mode 100644 index 0000000..9126ff7 --- /dev/null +++ b/kaneton/core/include/types.h @@ -0,0 +1,58 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/include/types.h + * + * created julien quintard [wed jun 6 22:38:37 2007] + * updated julien quintard [wed mar 2 20:26:08 2011] + */ + +#ifndef CORE_TYPES_H +#define CORE_TYPES_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * the general-purpose size and offset types. + */ + +typedef t_uint32 t_size; +typedef t_uint32 t_offset; + +/* + * the types below are used in a variety of situations. however their + * names are pretty clear about their usage. + */ + +typedef t_uint8 t_boolean; +typedef t_uint32 t_options; +typedef t_uint32 t_permissions; +typedef t_uint32 t_type; +typedef t_uint32 t_state; +typedef t_uint32 t_status; +typedef t_uint32 t_class; +typedef t_uint32 t_behaviour; +typedef t_uint32 t_priority; +typedef t_uint32 t_quantum; +typedef t_uint32 t_operations; +typedef t_uint64 t_timeslice; +typedef t_sint32 t_value; +typedef t_sint64 t_delay; +typedef t_uint32 t_width; +typedef t_uint64 t_flags; +typedef t_uint32 t_privilege; +typedef t_uint32 t_data; + +#endif diff --git a/kaneton/core/include/wait.h b/kaneton/core/include/wait.h new file mode 100644 index 0000000..69b836c --- /dev/null +++ b/kaneton/core/include/wait.h @@ -0,0 +1,123 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/include/wait.h + * + * created julien quintard [wed jun 6 15:48:52 2007] + * updated julien quintard [fri dec 10 11:48:43 2010] + */ + +#ifndef CORE_WAIT_H +#define CORE_WAIT_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include +#include + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * these macro indicate the state of the task/thread to wait for. + */ + +#define WAIT_STATE_NONE 0 +#define WAIT_STATE_START 1 +#define WAIT_STATE_STOP 2 +#define WAIT_STATE_DEATH 4 +#define WAIT_STATE_ANY (WAIT_STATE_START | \ + WAIT_STATE_START | \ + WAIT_STATE_DEATH) + +/* + * this value is used to indicate an unknown or irrelevant value. + * + * let's recall that the 'value' is the value returned through the exit() + * function. this value is passed to the waited thread. + */ + +#define WAIT_VALUE_UNKNOWN -1 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function returns the identifier of the task that has been + * waiting for. + */ + +#define WAIT_TASK(_wait_) \ + (_wait_)->id.task + +/* + * this macro-function returns the identifier of the thread that has + * been waiting for. + */ + +#define WAIT_THREAD(_wait_) \ + (_wait_)->id.thread + +/* + * this macro-function returns the reason that led to the wait to + * succeed. indeed, the wait() functions take a set of states for + * which the wait will succeed, should the target's state change + * to one of these states. + * + * for example, a wait for the stop or block state would provide the + * state block in the cause should the task/thread have blocked. + */ + +#define WAIT_CAUSE(_wait_) \ + (_wait_)->cause + +/* + * this macro-function returns the value which has been returned + * by a task/thread after it exited. + */ + +#define WAIT_VALUE(_wait_) \ + (_wait_)->value + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * the wait type that is returned to the caller. + * + * the 'id' attribute contains either the task or thread identifier + * depending on the target i.e the function used: either task_wait() or + * thread_wait(). + * + * the 'state' indicates the state for which the caller is waiting for. + * + * the 'cause' indicates what state change caused the wait to succeed i.e + * the cause is obviously one of the states included in 'state'. + * + * finally 'value' is the task/thread exit() value that is passed back to the + * waiting thread. + */ + +typedef struct +{ + union + { + i_task task; + i_thread thread; + } id; + + t_state state; + t_state cause; + t_value value; +} s_wait; + +#endif diff --git a/kaneton/core/interface/Makefile b/kaneton/core/interface/Makefile new file mode 100644 index 0000000..98b43ff --- /dev/null +++ b/kaneton/core/interface/Makefile @@ -0,0 +1,76 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/buckman/kaneton/kaneton/core/interface/Makefile +# +# created julien quintard [thu jun 28 20:32:46 2007] +# updated matthieu bucchianeri [sat sep 1 12:30:53 2007] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/core/interface + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +INTERFACE_C := interface.c + +INTERFACE_O := $(INTERFACE_C:.c=.o) + +INTERFACE_INCLUDE := $(_CORE_INCLUDE_DIR_)/interface.h + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(_INTERFACE_LO_) + +$(_INTERFACE_LO_): $(INTERFACE_O) + $(call env_remove,$(_INTERFACE_LO_),) + + $(call env_archive,$(_INTERFACE_LO_),$(INTERFACE_O),) + +clear: + $(call env_remove,$(INTERFACE_O),) + + $(call env_remove,$(_INTERFACE_LO_),) + + $(call env_purge,) + +prototypes: + $(call env_prototypes,$(INTERFACE_INCLUDE),) + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(INTERFACE_C),) + +dependencies: + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/core/interface/interface.c b/kaneton/core/interface/interface.c new file mode 100644 index 0000000..e15adde --- /dev/null +++ b/kaneton/core/interface/interface.c @@ -0,0 +1,1055 @@ +/* + * ---------- information ----------------------------------------------------- + * + * [XXX:improvements] the whole manager should be re-developed! + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- externs --------------------------------------------------------- + */ + +extern m_kernel _kernel; + +/* + * ---------- functions ------------------------------------------------------- + */ +/* + * this function launchs the as_copy() function. + */ + +t_error interface_as_copy(o_syscall* message) +{ + t_error error; + + error = as_copy(message->u.request.u.as_copy.arg1, + message->u.request.u.as_copy.arg2, + message->u.request.u.as_copy.arg3, + message->u.request.u.as_copy.arg4, + message->u.request.u.as_copy.arg5); + + message->u.reply.error = error; + + return (ERROR_OK); +} + +/* + * this function launchs the as_release() function. + */ + +t_error interface_as_release(o_syscall* message) +{ + t_error error; + + error = as_release(message->u.request.u.as_release.arg1); + + message->u.reply.error = error; + + return (ERROR_OK); +} + +/* + * this function get the task attribute of the o_as object. + */ + +t_error interface_as_attribute_task(o_syscall* message) +{ + o_as* o; + + if (as_get(message->u.request.u.as_attribute_task.arg1, &o) != ERROR_OK) + { + message->u.reply.error = ERROR_KO; + } + else + { + message->u.reply.error = ERROR_OK; + message->u.reply.u.as_attribute_task.result1 = o->task; + } + + return (ERROR_OK); +} + +/* + * this function launchs the event_reserve() function. + */ + +t_error interface_event_reserve(o_syscall* message) +{ + t_error error; + + error = event_reserve(message->u.request.u.event_reserve.arg1, + message->u.request.u.event_reserve.arg2, + message->u.request.u.event_reserve.arg3, + message->u.request.u.event_reserve.arg4); + + message->u.reply.error = error; + + return (ERROR_OK); +} + +/* + * this function launchs the event_release() function. + */ + +t_error interface_event_release(o_syscall* message) +{ + t_error error; + + error = event_release(message->u.request.u.event_release.arg1); + + message->u.reply.error = error; + + return (ERROR_OK); +} + +/* + * this function get the type attribute of the o_event object. + */ + +t_error interface_event_attribute_type(o_syscall* message) +{ + o_event* o; + + if (event_get(message->u.request.u.event_attribute_type.arg1, &o) != ERROR_OK) + { + message->u.reply.error = ERROR_KO; + } + else + { + message->u.reply.error = ERROR_OK; + message->u.reply.u.event_attribute_type.result1 = o->type; + } + + return (ERROR_OK); +} + +/* + * this function get the handler attribute of the o_event object. + */ + +t_error interface_event_attribute_handler(o_syscall* message) +{ + o_event* o; + + if (event_get(message->u.request.u.event_attribute_handler.arg1, &o) != ERROR_OK) + { + message->u.reply.error = ERROR_KO; + } + else + { + message->u.reply.error = ERROR_OK; + message->u.reply.u.event_attribute_handler.result1 = o->handler; + } + + return (ERROR_OK); +} + +/* + * this function get the data attribute of the o_event object. + */ + +t_error interface_event_attribute_data(o_syscall* message) +{ + o_event* o; + + if (event_get(message->u.request.u.event_attribute_data.arg1, &o) != ERROR_OK) + { + message->u.reply.error = ERROR_KO; + } + else + { + message->u.reply.error = ERROR_OK; + message->u.reply.u.event_attribute_data.result1 = o->data; + } + + return (ERROR_OK); +} + +/* + * this function launchs the map_reserve() function. + */ + +t_error interface_map_reserve(o_syscall* message) +{ + t_error error; + t_vaddr result1; + + error = map_reserve(message->u.request.u.map_reserve.arg1, + message->u.request.u.map_reserve.arg2, + message->u.request.u.map_reserve.arg3, + message->u.request.u.map_reserve.arg4, + &result1); + + message->u.reply.error = error; + message->u.reply.u.map_reserve.result1 = result1; + + return (ERROR_OK); +} + +/* + * this function launchs the map_release() function. + */ + +t_error interface_map_release(o_syscall* message) +{ + t_error error; + + error = map_release(message->u.request.u.map_release.arg1, + message->u.request.u.map_release.arg2); + + message->u.reply.error = error; + + return (ERROR_OK); +} + +/* + * this function launchs the map_resize() function. + */ + +t_error interface_map_resize(o_syscall* message) +{ + t_error error; + t_vaddr result1; + + error = map_resize(message->u.request.u.map_resize.arg1, + message->u.request.u.map_resize.arg2, + message->u.request.u.map_resize.arg3, + &result1); + + message->u.reply.error = error; + message->u.reply.u.map_resize.result1 = result1; + + return (ERROR_OK); +} + +/* + * this function launchs the region_reserve() function. + */ + +t_error interface_region_reserve(o_syscall* message) +{ + t_error error; + i_region result1; + + error = region_reserve(message->u.request.u.region_reserve.arg1, + message->u.request.u.region_reserve.arg2, + message->u.request.u.region_reserve.arg3, + message->u.request.u.region_reserve.arg4, + message->u.request.u.region_reserve.arg5, + message->u.request.u.region_reserve.arg6, + &result1); + + message->u.reply.error = error; + message->u.reply.u.region_reserve.result1 = result1; + + return (ERROR_OK); +} + +/* + * this function launchs the region_release() function. + */ + +t_error interface_region_release(o_syscall* message) +{ + t_error error; + + error = region_release(message->u.request.u.region_release.arg1, + message->u.request.u.region_release.arg2); + + message->u.reply.error = error; + + return (ERROR_OK); +} + +/* + * this function launchs the scheduler_quantum() function. + */ + +t_error interface_scheduler_quantum(o_syscall* message) +{ + t_error error; + + error = scheduler_quantum(message->u.request.u.scheduler_quantum.arg1); + + message->u.reply.error = error; + + return (ERROR_OK); +} + +/* + * this function launchs the scheduler_yield() function. + */ + +t_error interface_scheduler_yield(o_syscall* message) +{ + t_error error; + + error = scheduler_yield(); + + message->u.reply.error = error; + + return (ERROR_OK); +} + +/* + * this function launchs the scheduler_update() function. + */ + +t_error interface_scheduler_update(o_syscall* message) +{ + t_error error; + + error = scheduler_update(message->u.request.u.scheduler_update.arg1); + + message->u.reply.error = error; + + return (ERROR_OK); +} + +/* + * this function launchs the segment_clone() function. + */ + +t_error interface_segment_clone(o_syscall* message) +{ + t_error error; + i_segment result1; + + error = segment_clone(message->u.request.u.segment_clone.arg1, + message->u.request.u.segment_clone.arg2, + &result1); + + message->u.reply.error = error; + message->u.reply.u.segment_clone.result1 = result1; + + return (ERROR_OK); +} + +/* + * this function launchs the segment_give() function. + */ + +t_error interface_segment_give(o_syscall* message) +{ + t_error error; + + error = segment_give(message->u.request.u.segment_give.arg1, + message->u.request.u.segment_give.arg2); + + message->u.reply.error = error; + + return (ERROR_OK); +} + +/* + * this function launchs the segment_copy() function. + */ + +t_error interface_segment_copy(o_syscall* message) +{ + t_error error; + + error = segment_copy(message->u.request.u.segment_copy.arg1, + message->u.request.u.segment_copy.arg2, + message->u.request.u.segment_copy.arg3, + message->u.request.u.segment_copy.arg4, + message->u.request.u.segment_copy.arg5); + + message->u.reply.error = error; + + return (ERROR_OK); +} + +/* + * this function launchs the segment_release() function. + */ + +t_error interface_segment_release(o_syscall* message) +{ + t_error error; + + error = segment_release(message->u.request.u.segment_release.arg1); + + message->u.reply.error = error; + + return (ERROR_OK); +} + +/* + * this function launchs the segment_perms() function. + */ + +t_error interface_segment_permissions(o_syscall* message) +{ + t_error error; + + error = segment_permissions(message->u.request.u.segment_permissions.arg1, + message->u.request.u.segment_permissions.arg2); + + message->u.reply.error = error; + + return (ERROR_OK); +} + +/* + * this function get the asid attribute of the o_segment object. + */ + +t_error interface_segment_attribute_as(o_syscall* message) +{ + o_segment* o; + + if (segment_get(message->u.request.u.segment_attribute_as.arg1, &o) != ERROR_OK) + { + message->u.reply.error = ERROR_KO; + } + else + { + message->u.reply.error = ERROR_OK; + message->u.reply.u.segment_attribute_as.result1 = o->as; + } + + return (ERROR_OK); +} + +/* + * this function get the address attribute of the o_segment object. + */ + +t_error interface_segment_attribute_address(o_syscall* message) +{ + o_segment* o; + + if (segment_get(message->u.request.u.segment_attribute_address.arg1, &o) != ERROR_OK) + { + message->u.reply.error = ERROR_KO; + } + else + { + message->u.reply.error = ERROR_OK; + message->u.reply.u.segment_attribute_address.result1 = o->address; + } + + return (ERROR_OK); +} + +/* + * this function get the size attribute of the o_segment object. + */ + +t_error interface_segment_attribute_size(o_syscall* message) +{ + o_segment* o; + + if (segment_get(message->u.request.u.segment_attribute_size.arg1, &o) != ERROR_OK) + { + message->u.reply.error = ERROR_KO; + } + else + { + message->u.reply.error = ERROR_OK; + message->u.reply.u.segment_attribute_size.result1 = o->size; + } + + return (ERROR_OK); +} + +/* + * this function get the perms attribute of the o_segment object. + */ + +t_error interface_segment_attribute_permissions(o_syscall* message) +{ + o_segment* o; + + if (segment_get(message->u.request.u.segment_attribute_permissions.arg1, &o) != ERROR_OK) + { + message->u.reply.error = ERROR_KO; + } + else + { + message->u.reply.error = ERROR_OK; + message->u.reply.u.segment_attribute_permissions.result1 = o->permissions; + } + + return (ERROR_OK); +} + +/* + * this function launchs the task_reserve() function. + */ + +t_error interface_task_reserve(o_syscall* message) +{ + t_error error; + i_task result1; + + error = task_reserve(message->u.request.u.task_reserve.arg1, + message->u.request.u.task_reserve.arg2, + message->u.request.u.task_reserve.arg3, + &result1); + + message->u.reply.error = error; + message->u.reply.u.task_reserve.result1 = result1; + + return (ERROR_OK); +} + +/* + * this function launchs the task_release() function. + */ + +t_error interface_task_release(o_syscall* message) +{ + t_error error; + + error = task_release(message->u.request.u.task_release.arg1); + + message->u.reply.error = error; + + return (ERROR_OK); +} + +/* + * this function launchs the task_priority() function. + */ + +t_error interface_task_priority(o_syscall* message) +{ + t_error error; + + error = task_priority(message->u.request.u.task_priority.arg1, + message->u.request.u.task_priority.arg2); + + message->u.reply.error = error; + + return (ERROR_OK); +} + +/* + * this function get the parent attribute of the o_task object. + */ + +t_error interface_task_attribute_parent(o_syscall* message) +{ + o_task* o; + + if (task_get(message->u.request.u.task_attribute_parent.arg1, &o) != ERROR_OK) + { + message->u.reply.error = ERROR_KO; + } + else + { + message->u.reply.error = ERROR_OK; + message->u.reply.u.task_attribute_parent.result1 = o->parent; + } + + return (ERROR_OK); +} + +/* + * this function get the class attribute of the o_task object. + */ + +t_error interface_task_attribute_class(o_syscall* message) +{ + o_task* o; + + if (task_get(message->u.request.u.task_attribute_class.arg1, &o) != ERROR_OK) + { + message->u.reply.error = ERROR_KO; + } + else + { + message->u.reply.error = ERROR_OK; + message->u.reply.u.task_attribute_class.result1 = o->class; + } + + return (ERROR_OK); +} + +/* + * this function get the behav attribute of the o_task object. + */ + +t_error interface_task_attribute_behaviour(o_syscall* message) +{ + o_task* o; + + if (task_get(message->u.request.u.task_attribute_behaviour.arg1, &o) != ERROR_OK) + { + message->u.reply.error = ERROR_KO; + } + else + { + message->u.reply.error = ERROR_OK; + message->u.reply.u.task_attribute_behaviour.result1 = o->behaviour; + } + + return (ERROR_OK); +} + +/* + * this function get the prior attribute of the o_task object. + */ + +t_error interface_task_attribute_priority(o_syscall* message) +{ + o_task* o; + + if (task_get(message->u.request.u.task_attribute_priority.arg1, &o) != ERROR_OK) + { + message->u.reply.error = ERROR_KO; + } + else + { + message->u.reply.error = ERROR_OK; + message->u.reply.u.task_attribute_priority.result1 = o->priority; + } + + return (ERROR_OK); +} + +/* + * this function get the as attribute of the o_task object. + */ + +t_error interface_task_attribute_as(o_syscall* message) +{ + o_task* o; + + if (task_get(message->u.request.u.task_attribute_as.arg1, &o) != ERROR_OK) + { + message->u.reply.error = ERROR_KO; + } + else + { + message->u.reply.error = ERROR_OK; + message->u.reply.u.task_attribute_as.result1 = o->as; + } + + return (ERROR_OK); +} + +/* + * this function get the sched attribute of the o_task object. + */ + +t_error interface_task_attribute_sched(o_syscall* message) +{ + o_task* o; + + if (task_get(message->u.request.u.task_attribute_sched.arg1, &o) != ERROR_OK) + { + message->u.reply.error = ERROR_KO; + } + else + { + message->u.reply.error = ERROR_OK; + message->u.reply.u.task_attribute_sched.result1 = o->state; + } + + return (ERROR_OK); +} + +/* + * this function launchs the thread_release() function. + */ + +t_error interface_thread_release(o_syscall* message) +{ + t_error error; + + error = thread_release(message->u.request.u.thread_release.arg1); + + message->u.reply.error = error; + + return (ERROR_OK); +} + +/* + * this function launchs the thread_priority() function. + */ + +t_error interface_thread_priority(o_syscall* message) +{ + t_error error; + + error = thread_priority(message->u.request.u.thread_priority.arg1, + message->u.request.u.thread_priority.arg2); + + message->u.reply.error = error; + + return (ERROR_OK); +} + +/* + * this function launchs the thread_load() function. + */ + +t_error interface_thread_load(o_syscall* message) +{ + t_error error; + + error = thread_load(message->u.request.u.thread_load.arg1, + message->u.request.u.thread_load.arg2); + + message->u.reply.error = error; + + return (ERROR_OK); +} + +/* + * this function launchs the thread_store() function. + */ + +t_error interface_thread_store(o_syscall* message) +{ + t_error error; + s_thread_context result1; + + error = thread_store(message->u.request.u.thread_store.arg1, + &result1); + + message->u.reply.error = error; + message->u.reply.u.thread_store.result1 = result1; + + return (ERROR_OK); +} + +/* + * this function get the taskid attribute of the o_thread object. + */ + +t_error interface_thread_attribute_task(o_syscall* message) +{ + o_thread* o; + + if (thread_get(message->u.request.u.thread_attribute_task.arg1, &o) != ERROR_OK) + { + message->u.reply.error = ERROR_KO; + } + else + { + message->u.reply.error = ERROR_OK; + message->u.reply.u.thread_attribute_task.result1 = o->task; + } + + return (ERROR_OK); +} + +/* + * this function get the prior attribute of the o_thread object. + */ + +t_error interface_thread_attribute_priority(o_syscall* message) +{ + o_thread* o; + + if (thread_get(message->u.request.u.thread_attribute_priority.arg1, &o) != ERROR_OK) + { + message->u.reply.error = ERROR_KO; + } + else + { + message->u.reply.error = ERROR_OK; + message->u.reply.u.thread_attribute_priority.result1 = o->priority; + } + + return (ERROR_OK); +} + +/* + * this function get the sched attribute of the o_thread object. + */ + +t_error interface_thread_attribute_state(o_syscall* message) +{ + o_thread* o; + + if (thread_get(message->u.request.u.thread_attribute_state.arg1, &o) != ERROR_OK) + { + message->u.reply.error = ERROR_KO; + } + else + { + message->u.reply.error = ERROR_OK; + message->u.reply.u.thread_attribute_state.result1 = o->state; + } + + return (ERROR_OK); +} + +/* + * this function launchs the timer_reserve() function. + */ + +t_error interface_timer_reserve(o_syscall* message) +{ + t_error error; + i_timer result1; + + error = timer_reserve(message->u.request.u.timer_reserve.arg1, + message->u.request.u.timer_reserve.arg2, + message->u.request.u.timer_reserve.arg3, + message->u.request.u.timer_reserve.arg4, + message->u.request.u.timer_reserve.arg5, + &result1); + + message->u.reply.error = error; + message->u.reply.u.timer_reserve.result1 = result1; + + return (ERROR_OK); +} + +/* + * this function launchs the timer_release() function. + */ + +t_error interface_timer_release(o_syscall* message) +{ + t_error error; + + error = timer_release(message->u.request.u.timer_release.arg1); + + message->u.reply.error = error; + + return (ERROR_OK); +} + +/* + * this function get the delay attribute of the o_timer object. + */ + +t_error interface_timer_attribute_delay(o_syscall* message) +{ + o_timer* o; + + if (timer_get(message->u.request.u.timer_attribute_delay.arg1, &o) != ERROR_OK) + { + message->u.reply.error = ERROR_KO; + } + else + { + message->u.reply.error = ERROR_OK; + message->u.reply.u.timer_attribute_delay.result1 = o->delay; + } + + return (ERROR_OK); +} + +/* + * this function get the repeat attribute of the o_timer object. + */ + +t_error interface_timer_attribute_repeat(o_syscall* message) +{ + o_timer* o; + + if (timer_get(message->u.request.u.timer_attribute_repeat.arg1, &o) != ERROR_OK) + { + message->u.reply.error = ERROR_KO; + } + else + { + message->u.reply.error = ERROR_OK; + message->u.reply.u.timer_attribute_repeat.result1 = o->repeat; + } + + return (ERROR_OK); +} + +/* + * this function get the type attribute of the o_timer object. + */ + +t_error interface_timer_attribute_type(o_syscall* message) +{ + o_timer* o; + + if (timer_get(message->u.request.u.timer_attribute_type.arg1, &o) != ERROR_OK) + { + message->u.reply.error = ERROR_KO; + } + else + { + message->u.reply.error = ERROR_OK; + message->u.reply.u.timer_attribute_type.result1 = o->type; + } + + return (ERROR_OK); +} + +/* + * this function get the handler attribute of the o_timer object. + */ + +t_error interface_timer_attribute_handler(o_syscall* message) +{ + o_timer* o; + + if (timer_get(message->u.request.u.timer_attribute_handler.arg1, &o) != ERROR_OK) + { + message->u.reply.error = ERROR_KO; + } + else + { + message->u.reply.error = ERROR_OK; + message->u.reply.u.timer_attribute_handler.result1 = o->handler; + } + + return (ERROR_OK); +} + +/* + * this function get the data attribute of the o_timer object. + */ + +t_error interface_timer_attribute_data(o_syscall* message) +{ + o_timer* o; + + if (timer_get(message->u.request.u.timer_attribute_data.arg1, &o) != ERROR_OK) + { + message->u.reply.error = ERROR_KO; + } + else + { + message->u.reply.error = ERROR_OK; + message->u.reply.u.timer_attribute_data.result1 = o->data; + } + + return (ERROR_OK); +} + +/* + * this structure dispatchs incoming system calls. + */ + +t_interface_dispatch dispatch[] = +{ + interface_as_copy, + interface_as_release, + interface_as_attribute_task, + interface_event_reserve, + interface_event_release, + interface_event_attribute_type, + interface_event_attribute_handler, + interface_event_attribute_data, + interface_map_reserve, + interface_map_release, + interface_map_resize, + interface_region_reserve, + interface_region_release, + interface_scheduler_quantum, + interface_scheduler_yield, + interface_scheduler_update, + interface_segment_clone, + interface_segment_give, + interface_segment_copy, + interface_segment_release, + interface_segment_permissions, + interface_segment_attribute_as, + interface_segment_attribute_address, + interface_segment_attribute_size, + interface_segment_attribute_permissions, + interface_task_reserve, + interface_task_release, + interface_task_priority, + interface_task_attribute_parent, + interface_task_attribute_class, + interface_task_attribute_behaviour, + interface_task_attribute_priority, + interface_task_attribute_as, + interface_task_attribute_sched, + interface_thread_release, + interface_thread_priority, + interface_thread_load, + interface_thread_store, + interface_thread_attribute_task, + interface_thread_attribute_priority, + interface_thread_attribute_state, + interface_timer_reserve, + interface_timer_release, + interface_timer_attribute_delay, + interface_timer_attribute_repeat, + interface_timer_attribute_type, + interface_timer_attribute_handler, + interface_timer_attribute_data, +}; + +/* + * this function receives, dispatch and reply incoming system calls. + * + * steps: + * + * 1) check for syscall correctness. + * 2) execute the system call. + * 3) send the reply. + */ + +t_error interface_notify(t_uint8* buffer, + t_vsize size, + i_node source) +{ + o_syscall* message = (o_syscall*)buffer; + + /* + * 1) + */ + + /* if (size < sizeof (o_syscall)) + return (ERROR_KO); */ + + if (message->u.request.operation >= INTERFACE_NSYSCALLS) + return (ERROR_KO); + + /* + * 2) + */ + t_uint32 op = message->u.request.operation; + (void) op; /* UNUSED VAR */ + + if (dispatch[message->u.request.operation](message) != ERROR_OK) + return (ERROR_KO); + + /* + * 3) + */ + + if (message_send(_kernel.task, + source, + MESSAGE_TYPE_INTERFACE, + (t_vaddr)message, + sizeof (o_syscall)) != ERROR_OK) + return (ERROR_KO); + + return (ERROR_OK); +} + +/* + * this function initialises the interface manager. + * + */ + +t_error interface_initialize(void) +{ + if (message_register(_kernel.task, 0, sizeof (o_syscall)) != ERROR_OK) + return (ERROR_KO); + + return (ERROR_OK); +} + +/* + * this function cleans the interface manager. + */ + +t_error interface_clean(void) +{ + return (ERROR_OK); +} diff --git a/kaneton/core/io/Makefile b/kaneton/core/io/Makefile new file mode 100644 index 0000000..5bba8e8 --- /dev/null +++ b/kaneton/core/io/Makefile @@ -0,0 +1,76 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/buckman/kaneton/kaneton/core/io/Makefile +# +# created julien quintard [thu jun 28 20:33:15 2007] +# updated matthieu bucchianeri [sat sep 1 12:30:45 2007] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/core/io + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +IO_C := io.c + +IO_O := $(IO_C:.c=.o) + +IO_INCLUDE := $(_CORE_INCLUDE_DIR_)/io.h + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(_IO_LO_) + +$(_IO_LO_): $(IO_O) + $(call env_remove,$(_IO_LO_),) + + $(call env_archive,$(_IO_LO_),$(IO_O),) + +clear: + $(call env_remove,$(IO_O),) + + $(call env_remove,$(_IO_LO_),) + + $(call env_purge,) + +prototypes: + $(call env_prototypes,$(IO_INCLUDE),) + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(IO_C),) + +dependencies: + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/core/io/io.c b/kaneton/core/io/io.c new file mode 100644 index 0000000..7dc0285 --- /dev/null +++ b/kaneton/core/io/io.c @@ -0,0 +1,210 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/io/io.c + * + * created matthieu bucchianeri [sat jul 29 17:59:35 2006] + * updated julien quintard [sun jan 30 20:07:13 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * the I/O manager provides functionalities for grant and denying access + * to low-level hardware i.e referred to as ports. + * + * since low-level I/O are hardware-specific, the manager relies on the + * machine for implementating most of the manager's functionalities. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * include the machine-specific definitions required by the core. + */ + +machine_include(io); + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * I/O manager structure. + */ + +m_io _io; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function grants access on the given port and width to the + * identified task. + * + * steps: + * + * 1) call the machine. + */ + +t_error io_grant(i_task task, + i_port port, + t_width width) +{ + /* + * 1) + */ + + if (machine_call(io, grant, task, port, width) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function denies access on a given port to the task. + * + * steps: + * + * 1) call the machine. + */ + +t_error io_deny(i_task task, + i_port port, + t_width width) +{ + /* + * 1) + */ + + if (machine_call(io, deny, task, port, width) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function reads data from an I/O port. + * + * steps: + * + * 0) verify the arguments + * 1) call the machine. + */ + +t_error io_read(i_task task, + i_port port, + t_width width, + void* data) +{ + /* + * 0) + */ + + if (data == NULL) + CORE_ESCAPE("the 'data' argument is null"); + + /* + * 1) + */ + + if (machine_call(io, read, task, port, width, data) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function writes data to an I/O port. + * + * steps: + * + * 1) call the machine. + */ + +t_error io_write_8(i_task task, + i_port port, + t_width width, + t_uint64 data) +{ + /* + * 1) + */ + + if (machine_call(io, write, task, port, width, data) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function initializes the I/O manager. + * + * steps: + * + * 1) display a message. + * 2) initialize memory for the manager structure. + * 3) call the machine. + */ + +t_error io_initialize(void) +{ + /* + * 1) + */ + + module_call(console, message, + '+', "initializing the I/O manager\n"); + + /* + * 2) + */ + + memset(&_io, 0x0, sizeof (m_io)); + + /* + * 3) + */ + + if (machine_call(io, initialize) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function cleans the I/O manager. + * + * steps: + * + * 1) display a message. + * 2) call the machine. + */ + +t_error io_clean(void) +{ + /* + * 1) + */ + + module_call(console, message, + '+', "cleaning the I/O manager\n"); + + /* + * 2) + */ + + if (machine_call(io, clean) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} diff --git a/kaneton/core/kernel/Makefile b/kaneton/core/kernel/Makefile new file mode 100644 index 0000000..7af23d8 --- /dev/null +++ b/kaneton/core/kernel/Makefile @@ -0,0 +1,76 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/lec_l/kaneton/kaneton/core/kernel/Makefile +# +# created julien quintard [sun jun 10 16:57:54 2007] +# updated laurent lec [wed jun 4 17:24:24 2008] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/core/kernel + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +KERNEL_C := kernel.c + +KERNEL_O := $(KERNEL_C:.c=.o) + +KERNEL_INCLUDE := $(_CORE_INCLUDE_DIR_)/kernel.h + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(_KERNEL_LO_) + +$(_KERNEL_LO_): $(KERNEL_O) + $(call env_remove,$(_KERNEL_LO_),) + + $(call env_archive,$(_KERNEL_LO_),$(KERNEL_O),) + +clear: + $(call env_remove,$(KERNEL_O),) + + $(call env_remove,$(_KERNEL_LO_),) + + $(call env_purge,) + +prototypes: + $(call env_prototypes,$(KERNEL_INCLUDE),) + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(KERNEL_C),) + +dependencies: + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/core/kernel/kernel.c b/kaneton/core/kernel/kernel.c new file mode 100644 index 0000000..5a01291 --- /dev/null +++ b/kaneton/core/kernel/kernel.c @@ -0,0 +1,471 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/kernel/kernel.c + * + * created julien quintard [fri feb 11 03:04:40 2005] + * updated julien quintard [thu apr 7 14:23:31 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file contains the kernel manager's code. + * + * this manager, unlike the others, does not manage a specific kaneton + * object but rather the whole kernel. indeed, this manager does not provide + * any functionality but to initialize and clean the kernel by calling + * all the other managers. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * include the machine-specific definitions required by the core. + */ + +machine_include(kernel); + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the init structure. + */ + +extern s_init* _init; + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the kernel manager. + */ + +m_kernel _kernel; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function dumps the kernel manager. + * + * steps: + * + * 1) build the endian string. + * 2) display information. + * 3) call the machine. + */ + +t_error kernel_dump(void) +{ + char* endian; + + /* + * 1) + */ + + switch (___kaneton$endian) + { + case ENDIAN_LITTLE: + { + endian = "little"; + + break; + } + case ENDIAN_BIG: + { + endian = "big"; + + break; + } + default: + CORE_ESCAPE("unknown endianness '%u'", + ___kaneton$endian); + } + + /* + * 2) + */ + + module_call(console, message, + '#', + "kernel manager:\n"); + + module_call(console, message, + '#', + " core: cell(%qd) task(%qd) as(%qd) thread(%qd) " + "node(%qd:%qd) endian(%s) wordsz(%u) framesz(%u) pagesz(%u)\n", + _kernel.cell, + _kernel.task, _kernel.as, _kernel.thread, + _kernel.node.cell, _kernel.node.task, + endian, + ___kaneton$wordsz, ___kaneton$framesz, ___kaneton$pagesz); + + /* + * 3) + */ + + if (machine_call(kernel, dump) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function initializes the kernel manager by initializing every + * other manager. + * + * note that the kernel manager's components such as the task, thread, + * address space etc. are not reserved where one may expect to. for instance, + * the kernel task is reserve in task_initialize() along with the address + * space. however the kernel thread is reserved in thread_initialize(). + * + * in addition, the task_initialize() function injects the pre-reserved + * segments and regions passed by the boot loaded through the 'init' + * structure. + * + * steps: + * + * 1) initialize the kernel structure but also set the kernel + * identifiers---task, as and thread---as being not initialised. + * 2) initialize the identifier manager. + * 3) initialize the set manager. + * 4) initialize the address space manager. + * 5) initialize the segment manager. + * 6) initialize the region manager. + * 7) initialize the map manager + * 8) initialize the CPU manager. + * 9) initialize the task manager. + * 10) initialize the thread manager. + * 11) now that _everything_ related to memory manager is ready, set up + * the fine grain memory allocator in order to work with the kernel + * managers rather than the pre-allocated memory area provided by + * the boot loader. + * note that the task manager must have been initialized since it is + * its reponsability to reserve the kernel task, thread and address + * space and then to inject the init's segments and regions in the + * kernel's address space. + * 12) initialize the event manager. + * 13) initialize the timer manager. + * 14) initialize the clock manager. + * 15) initialize the I/O manager. + * 16) initialize the scheduler manager. + * 17) generate the random kaneton cell identifier and sets up the + * kaneton node identifier. + * 18) initialize the capability manager. + * 19) initialize the message manager. + * 20) call the machine. + */ + +t_error kernel_initialize(void) +{ + /* + * 1) + */ + + memset(&_kernel, 0x0, sizeof (m_kernel)); + + _kernel.task = ID_UNUSED; + _kernel.as = ID_UNUSED; + _kernel.thread = ID_UNUSED; + + /* + * 2) + */ + + if (id_initialize() != ERROR_OK) + CORE_ESCAPE("unable to initialize the identifier manager"); + + /* + * 3) + */ + + if (set_initialize() != ERROR_OK) + CORE_ESCAPE("unable to initialize the set manager"); + + /* + * 4) + */ + + if (as_initialize() != ERROR_OK) + CORE_ESCAPE("unable to initialize the address space manager"); + + /* + * 5) + */ + + if (segment_initialize(_init->pbase, + _init->psize) != ERROR_OK) + CORE_ESCAPE("unable to initialize the segment manager"); + + /* + * 6) + */ + + if (region_initialize(_init->vbase, + _init->vsize) != ERROR_OK) + CORE_ESCAPE("unable to initialize the region manager"); + + /* + * 7) + */ + + if (map_initialize() != ERROR_OK) + CORE_ESCAPE("unable to initialize the map manager"); + + /* + * 8) + */ + + if (cpu_initialize() != ERROR_OK) + CORE_ESCAPE("unable to initialize the CPU manager"); + + /* + * 9) + */ + + if (task_initialize() != ERROR_OK) + CORE_ESCAPE("unable to initialize the task manager"); + + /* + * 10) + */ + + if (thread_initialize() != ERROR_OK) + CORE_ESCAPE("unable to initialize the thread manager"); + + /* + * 11) + */ + + alloc_setup(); + + /* + * 12) + */ + + if (event_initialize() != ERROR_OK) + CORE_ESCAPE("unable to initialize the event manager"); + + /* + * 13) + */ + + if (timer_initialize() != ERROR_OK) + CORE_ESCAPE("unable to initialize the timer manager"); + + /* + * 14) + */ + + if (clock_initialize() != ERROR_OK) + CORE_ESCAPE("unable to initialize the clock manager"); + + /* + * 15) + */ + + if (io_initialize() != ERROR_OK) + CORE_ESCAPE("unable to initialize the I/O manager"); + + /* + * 16) + */ + + if (scheduler_initialize() != ERROR_OK) + CORE_ESCAPE("unable to initialize the scheduler manager"); + + /* + * 17) + */ + + random_seed(); + + _kernel.cell = random_generate(); + + _kernel.node.cell = _kernel.cell; + _kernel.node.task = _kernel.task; + + /* + * 18) + */ + + if (capability_initialize() != ERROR_OK) + CORE_ESCAPE("unable to initialize the capability manager"); + + /* + * 19) + */ + + if (message_initialize() != ERROR_OK) + CORE_ESCAPE("unable to initialize the message manager"); + + /* + * 20) + */ + + if (machine_call(kernel, initialize) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function cleans the kernel manager by cleaning every manager. + * + * steps: + * + * 1) call the machine. + * 2) clean the message manager. + * 3) clean the capability manager. + * 4) clean the scheduler manager. + * 5) clean the I/O manager. + * 6) clean the clock manager. + * 7) clean the timer manager. + * 8) clean the event manager. + * 9) clean the thread manager. + * 10) clean the task manager. + * 11) clean the CPU manager. + * 12) clean the map manager. + * 13) clean the region manager. + * 14) clean the segment manager. + * 15) clean the address space manager. + * 16) clean the set manager. + * 17) clean the identifier manager. + */ + +t_error kernel_clean(void) +{ + /* + * 1) + */ + + if (machine_call(kernel, clean) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 2) + */ + + if (message_clean() != ERROR_OK) + CORE_ESCAPE("unable to clean the message manager"); + + /* + * 3) + */ + + if (capability_clean() != ERROR_OK) + CORE_ESCAPE("unable to clean the capability manager"); + + /* + * 4) + */ + + if (scheduler_clean() != ERROR_OK) + CORE_ESCAPE("unable to clean the scheduler manager"); + + /* + * 5) + */ + + if (io_clean() != ERROR_OK) + CORE_ESCAPE("unable to clean the I/O manager"); + + /* + * 6) + */ + + if (clock_clean() != ERROR_OK) + CORE_ESCAPE("unable to clean the clock manager"); + + /* + * 7) + */ + + if (timer_clean() != ERROR_OK) + CORE_ESCAPE("unable to clean the timer manager"); + + /* + * 8) + */ + + if (event_clean() != ERROR_OK) + CORE_ESCAPE("unable to clean the event manager"); + + /* + * 9) + */ + + if (thread_clean() != ERROR_OK) + CORE_ESCAPE("unable to clean the thread manager"); + + /* + * 10) + */ + + if (task_clean() != ERROR_OK) + CORE_ESCAPE("unable to clean the task manager"); + + /* + * 11) + */ + + if (cpu_clean() != ERROR_OK) + CORE_ESCAPE("unable to clean the CPU manager"); + + /* + * 12) + */ + + if (map_clean() != ERROR_OK) + CORE_ESCAPE("unable to clean the map manager"); + + /* + * 13) + */ + + if (region_clean() != ERROR_OK) + CORE_ESCAPE("unable to clean the region manager"); + + /* + * 14) + */ + + if (segment_clean() != ERROR_OK) + CORE_ESCAPE("unable to clean the segment manager"); + + /* + * 15) + */ + + if (as_clean() != ERROR_OK) + CORE_ESCAPE("unable to clean the address space manager"); + + /* + * 16) + */ + + if (set_clean() != ERROR_OK) + CORE_ESCAPE("unable to clean the set manager"); + + /* + * 17) + */ + + if (id_clean() != ERROR_OK) + CORE_ESCAPE("unable to clean the identifier manager"); + + CORE_LEAVE(); +} diff --git a/kaneton/core/map/Makefile b/kaneton/core/map/Makefile new file mode 100644 index 0000000..75b1a5b --- /dev/null +++ b/kaneton/core/map/Makefile @@ -0,0 +1,76 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/buckman/kaneton/kaneton/core/map/Makefile +# +# created julien quintard [sun jun 10 16:58:56 2007] +# updated matthieu bucchianeri [sat sep 1 12:30:25 2007] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/core/map + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +MAP_C := map.c + +MAP_O := $(MAP_C:.c=.o) + +MAP_INCLUDE := $(_CORE_INCLUDE_DIR_)/map.h + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(_MAP_LO_) + +$(_MAP_LO_): $(MAP_O) + $(call env_remove,$(_MAP_LO_),) + + $(call env_archive,$(_MAP_LO_),$(MAP_O),) + +clear: + $(call env_remove,$(MAP_O),) + + $(call env_remove,$(_MAP_LO_),) + + $(call env_purge,) + +prototypes: + $(call env_prototypes,$(MAP_INCLUDE),) + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(MAP_C),) + +dependencies: + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/core/map/map.c b/kaneton/core/map/map.c new file mode 100644 index 0000000..28cfe96 --- /dev/null +++ b/kaneton/core/map/map.c @@ -0,0 +1,481 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/map/map.c + * + * created matthieu bucchianeri [sun feb 26 12:56:54 2006] + * updated julien quintard [fri apr 8 09:54:07 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * the map manager provides a layer on top of the segment and region managers. + * + * a map is an abstraction which couples a segment with a region. therefore, + * reserving a map implies reserving a segment of the map's size but also + * reserving a region mapping the segment. + * + * this manager offers three basic functionalities: reserving, releasing + * and resizing maps. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * include the machine-specific definitions required by the core. + */ + +machine_include(map); + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the map manager's structure. + */ + +m_map _map; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function reserve a map i.e a segment mapped through a region. + * + * steps: + * + * 0) verify the arguments. + * 1) reserve a segment. + * 2) reserve a region depending on the options. + * A) the region must be reserved at a precise location... + * a) either force the region at a specific location or; + * B) otherwise... + * a) reserve a region anywhere. + * b) retrieve the new region object. + * c) set the virtual address. + * 3) call the machine. + */ + +t_error map_reserve(i_as as, + t_options options, + t_vsize size, + t_permissions permissions, + t_vaddr* address) +{ + i_segment segment; + i_region region; + o_region* o; + + /* + * 0) + */ + + if (options & MAP_OPTION_INVALID) + CORE_ESCAPE("the 'options' argument is invalid"); + + if (permissions & PERMISSION_INVALID) + CORE_ESCAPE("the 'permissions' argument is invalid"); + + if (address == NULL) + CORE_ESCAPE("the 'address' argument is null"); + + if (size == 0) + CORE_LEAVE(); + + /* + * 1) + */ + + if (segment_reserve(as, + size, + permissions, + ((options & MAP_OPTION_SYSTEM) ? + SEGMENT_OPTION_SYSTEM : SEGMENT_OPTION_NONE), + &segment) != ERROR_OK) + CORE_ESCAPE("unable to reserve a segment"); + + /* + * 2) + */ + + if (options & MAP_OPTION_FORCE) + { + /* + * A) + */ + + /* + * a) + */ + + if (region_reserve(as, + segment, + 0, + REGION_OPTION_FORCE, + *address, + size, + ®ion) != ERROR_OK) + CORE_ESCAPE("unable to reserve the region"); + } + else + { + /* + * B) + */ + + /* + * a) + */ + + if (region_reserve(as, + segment, + 0, + REGION_OPTION_NONE, + 0, + size, + ®ion) != ERROR_OK) + CORE_ESCAPE("unable to reserve a region"); + + /* + * b) + */ + + if (region_get(as, region, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + /* + * c) + */ + + *address = o->address; + } + + /* + * 3) + */ + + if (machine_call(map, reserve, + as, options, size, permissions, address) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function releases a map i.e the couple of segment and region. + * + * steps: + * + * 1) call the machine. + * 2) locate the region in which the given address lies. + * 3) retrieve the region object. + * 4) save the segment associated with this region. + * 5) release the region. + * 6) release the segment. + */ + +t_error map_release(i_as as, + t_vaddr address) +{ + i_region region; + i_segment segment; + o_region* o; + + /* + * 1) + */ + + if (machine_call(map, release, as, address) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 2) + */ + + if (region_locate(as, address, ®ion) == ERROR_FALSE) + CORE_ESCAPE("unable to locate the region in which the address lies"); + + /* + * 3) + */ + + if (region_get(as, region, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + /* + * 4) + */ + + segment = o->segment; + + /* + * 5) + */ + + if (region_release(as, region) != ERROR_OK) + CORE_ESCAPE("unable to release the region"); + + /* + * 6) + */ + + if (segment_release(segment) != ERROR_OK) + CORE_ESCAPE("unable to release the segment"); + + CORE_LEAVE(); +} + +/* + * this function resizes a given map identified by the map's virtual address. + * + * steps: + * + * 0) verify the arguments. + * 1) locate the region in which the given address lies. + * 2) retrieve the region object. + * 3) resize the segment mapped by the region. + * 4) resize the region depending on the fact that the segment has been + * relocated. + * A) if the segment has been relocated... + * a) save the current region's offset and options. + * b) the mapping is now invalid, so release the region. + * c) try to remap the region at the same address. + * i) if it is impossible and that mapping at this address was part of + * the request, then return an error. + * ii) otherwise, try to reserve another region anywhere. + * d) retrieve the reserved region object. + * e) set the new virtual address. + * B) otherwise... + * a) resize the region, possibly leading to a new region being reserved. + * b) retrieve the new region object, which could very much be the same + * as the previous one. + * c) set the new virtual address. + * 5) call the machine. + */ + +t_error map_resize(i_as as, + t_vaddr old, + t_vsize size, + t_vaddr* new) +{ + t_options options; + t_paddr offset; + i_segment segment; + i_region region; + o_region* o; + + /* + * 0) + */ + + if (new == NULL) + CORE_ESCAPE("the 'new' argument is null"); + + if (size == 0) + CORE_ESCAPE("unable to resize a map to a size of zero"); + + /* + * 1) + */ + + if (region_locate(as, old, ®ion) == ERROR_FALSE) + CORE_ESCAPE("unable to locate the region in which the address lies"); + + /* + * 2) + */ + + if (region_get(as, region, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + /* + * 3) + */ + + if (segment_resize(o->segment, + size, + SEGMENT_OPTION_NONE, + &segment) != ERROR_OK) + CORE_ESCAPE("unable to resize the segment"); + + /* + * 4) + */ + + if (segment != o->segment) + { + /* + * A) + */ + + /* + * a) + */ + + offset = o->offset; + options = o->options; + + /* + * b) + */ + + if (region_release(as, o->id) != ERROR_OK) + CORE_ESCAPE("unable to release the region"); + + /* + * c) + */ + + if (region_reserve(as, + segment, + offset, + options | REGION_OPTION_FORCE, + old, + size, + ®ion) != ERROR_OK) + { + /* + * i) + */ + + if (options & REGION_OPTION_FORCE) + CORE_ESCAPE("unable to resize while maintaining the region at " + "the previous location"); + + /* + * ii) + */ + + if (region_reserve(as, + segment, + offset, + options, + 0, + size, + ®ion) != ERROR_OK) + CORE_ESCAPE("unable to reserve a region"); + } + + /* + * d) + */ + + if (region_get(as, region, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + /* + * e) + */ + + *new = o->address; + } + else + { + /* + * B) + */ + + /* + * a) + */ + + if (region_resize(as, o->id, size, ®ion) != ERROR_OK) + CORE_ESCAPE("unable to resize the region"); + + /* + * b) + */ + + if (region_get(as, region, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + /* + * c) + */ + + *new = o->address; + } + + /* + * 5) + */ + + if (machine_call(map, resize, as, old, size, new) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function initialize the map manager. + * + * steps: + * + * 1) display a message. + * 2) initialize the map manager's structure. + * 3) call the machine. + */ + +t_error map_initialize(void) +{ + /* + * 1) + */ + + module_call(console, message, + '+', "initializing the map manager\n"); + + /* + * 2) + */ + + memset(&_map, 0x0, sizeof (m_map)); + + /* + * 3) + */ + + if (machine_call(map, initialize) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function release the map manager. + * + * steps: + * + * 1) display a message. + * 2) call the machine. + */ + +t_error map_clean(void) +{ + /* + * 1) + */ + + module_call(console, message, + '+', "cleaning the map manager\n"); + + /* + * 2) + */ + + if (machine_call(map, clean) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} diff --git a/kaneton/core/message/Makefile b/kaneton/core/message/Makefile new file mode 100644 index 0000000..537c6e9 --- /dev/null +++ b/kaneton/core/message/Makefile @@ -0,0 +1,76 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/buckman/kaneton/kaneton/core/message/Makefile +# +# created julien quintard [thu jun 28 20:34:12 2007] +# updated matthieu bucchianeri [sat sep 1 12:30:14 2007] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/core/message + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +MESSAGE_C := message.c + +MESSAGE_O := $(MESSAGE_C:.c=.o) + +MESSAGE_INCLUDE := $(_CORE_INCLUDE_DIR_)/message.h + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(_MESSAGE_LO_) + +$(_MESSAGE_LO_): $(MESSAGE_O) + $(call env_remove,$(_MESSAGE_LO_),) + + $(call env_archive,$(_MESSAGE_LO_),$(MESSAGE_O),) + +clear: + $(call env_remove,$(MESSAGE_O),) + + $(call env_remove,$(_MESSAGE_LO_),) + + $(call env_purge,) + +prototypes: + $(call env_prototypes,$(MESSAGE_INCLUDE),) + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(MESSAGE_C),) + +dependencies: + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/core/message/message.c b/kaneton/core/message/message.c new file mode 100644 index 0000000..d7beea7 --- /dev/null +++ b/kaneton/core/message/message.c @@ -0,0 +1,1029 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/message/message.c + * + * created matthieu bucchianeri [mon jul 23 11:37:30 2007] + * updated julien quintard [sun jan 30 20:10:24 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * [XXX:improvements] the whole manager should be re-developed! + */ + +/* + * ---------- information ----------------------------------------------------- + * + * the message manager provides a complete set of functions to send, receive, + * request and reply messages in different passing modes. + * + * the message manager interface is equivalent in many ways to the + * well-known parallel and distributed computing libraries like MPI, PVM etc. + * + * indeed, functions to send synchronously and asynchronously are provided + * as well as blocking and non-blocking functions. + * + * sending an asynchronous message means the kaneton microkernel will have + * to copy the message into an internal buffer and then to copy it out in + * the destination buffer of the receiver. + * + * in the other hand, sending a synchronous message means that nothing is + * done until the two parts, sender and receiver, are ready to exchange the + * message. then, the kernel just copies the message from the source buffer + * directly into the destination buffer. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +machine_include(message); + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * kernel manager structure + */ + +extern m_kernel _kernel; + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * message manager variable. + */ + +m_message _message; + +/* + * ---------- functions ------------------------------------------------------- + */ + + +/* + * this function retrieves a message box of a task by its type. + */ + +static t_error message_box(i_task task, + t_type type, + o_message_type** o) +{ + t_id typeid = type; + o_task* otask; + + assert(o != NULL); + + if (task_get(task, &otask) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (set_get(otask->messages, typeid, (void**)o) != ERROR_OK) + CORE_ESCAPE("XXX"); + + CORE_LEAVE(); +} + +/* + * this function registers a new message type with its maximum length. + * + * only registered types of messages can be sent through the kernel. unknown + * types will be rejected. + * + * steps: + * + * 1) get the message box from the task. + * 2) check if this type is already registered. + * 3) build the message type structure. + * 4) add it to the task messages set. + * 5) call the machine dependent code. + * + */ + +t_error message_register(i_task task, + t_type type, + t_vsize size) +{ + t_id typeid = type; + o_task* o; + o_message_type msgtype; + + /* + * 1) + */ + + if (task_get(task, &o) != ERROR_OK) + CORE_ESCAPE("XXX"); + + /* + * 2) + */ + + if (set_exist(o->messages, typeid) == ERROR_TRUE) + CORE_ESCAPE("XXX"); + + /* + * 3) + */ + + msgtype.id = 0; + msgtype.type = typeid; + msgtype.size = size; + + if (set_reserve(pipe, SET_OPTION_ALLOCATE, sizeof (o_message), + &msgtype.queue) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (set_reserve(pipe, SET_OPTION_ALLOCATE, sizeof (o_message), + &msgtype.waiters) != ERROR_OK) + { + set_release(msgtype.queue); + + CORE_ESCAPE("XXX"); + } + + /* + * 4) + */ + + if (set_add(o->messages, &msgtype) != ERROR_OK) + { + set_release(msgtype.waiters), + set_release(msgtype.queue); + + CORE_ESCAPE("XXX"); + } + + /* + * 5) + */ + + if (machine_call(message, register, task, type, size) != ERROR_OK) + CORE_ESCAPE("XXX"); + + CORE_LEAVE(); +} + +/* + * this function returns the maximum size of a message in a message box. + * + */ + +t_error message_size(i_task task, + t_type type, + t_vsize* size) +{ + o_message_type* o; + + if (message_box(task, type, &o) != ERROR_OK) + CORE_ESCAPE("XXX"); + + *size = o->size; + + CORE_LEAVE(); +} + +/* + * this function free all the message types for one task. + * + * steps: + * + * 1) loop though the message types. + * 2) unblock waiters. + * 3) free outgoing messages. + * 4) release the sets. + * 5) call machine. + * + */ + +t_error message_flush(i_task task) +{ + o_task* o; + o_message_type* otype; + o_message* omsg; + s_iterator it; + + if (task_get(task, &o) != ERROR_OK) + CORE_ESCAPE("XXX"); + + /* + * 1) + */ + + while (set_head(o->messages, &it) == ERROR_TRUE) + { + if (set_object(o->messages, it, (void**)&otype) != ERROR_OK) + CORE_ESCAPE("XXX"); + + /* + * 2) + */ + + while (set_pick(otype->waiters, (void**)&omsg) != ERROR_OK) + { + if (message_return(omsg->blocked, ERROR_KO) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (set_pop(otype->waiters) != ERROR_OK) + CORE_ESCAPE("XXX"); + } + + /* + * 3) + */ + + while (set_pick(otype->queue, (void**)&omsg) != ERROR_OK) + { + if (set_pop(otype->waiters) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (omsg->as == ID_UNUSED) + { + if (message_return(omsg->blocked, ERROR_KO) != ERROR_OK) + CORE_ESCAPE("XXX"); + } + else + { + if (omsg->size != 0) + free(omsg->data); + } + } + + /* + * 4) + */ + + if (set_release(otype->waiters) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (set_release(otype->queue) != ERROR_OK) + CORE_ESCAPE("XXX"); + } + + /* + * 5) + */ + + if (machine_call(message, flush, task) != ERROR_OK) + CORE_ESCAPE("XXX"); + + CORE_LEAVE(); +} + +/* + * this function sends a message to a task. the message is delivered + * asynchronously and the function returns only when the buffer was safely + * copied. + * + * steps: + * + * 1) check message destination. + * 2) get the destination message box. + * 3) check the size. + * 4) system call special case. + * 5) if a thread is waiting for a message, then send it synchronously. + * 6) build the message. + * a) fill the buffer, intra-kernel case. + * b) fill the buffer, inter-as case. + * 7) push the message into the message box. + * 8) call the machine dependent code. + * + */ + +t_error message_send(i_task task, + i_node destination, + t_type type, + t_vaddr data, + t_vsize size) +{ + t_id typeid = type; + o_message_type* o; + o_task* otsk; + o_message msg; + t_setsz setsz; + + /* + * 1) + */ + + if (destination.cell != _kernel.cell) + { + /* XXX distr */ + CORE_ESCAPE("XXX"); + } + + /* + * 2) + */ + + if (message_box(destination.task, typeid, &o) != ERROR_OK) + CORE_ESCAPE("XXX"); + + /* + * 3) + */ + + if (size > o->size) + CORE_ESCAPE("XXX"); + + /* + * 4) + */ + + if (destination.task == _kernel.task && type == 0) + { + void* buffer; + i_node source; + + assert(task != _kernel.task); + assert(size != 0); + + source.cell = _kernel.cell; + source.task = task; + + if (task_get(task, &otsk) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if ((buffer = malloc(size)) == NULL) + CORE_ESCAPE("XXX"); + + if (as_read(otsk->as, data, size, buffer) != ERROR_OK) + { + free(buffer); + + CORE_ESCAPE("XXX"); + } + + if (interface_notify(buffer, size, source) != ERROR_OK) + CORE_ESCAPE("XXX"); + + free(buffer); + + CORE_LEAVE(); + } + + /* + * 5) + */ + + if (set_size(o->waiters, &setsz) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (setsz != 0) + { + if (message_transmit(task, destination, type, data, size) != ERROR_OK) + CORE_ESCAPE("XXX"); + + CORE_LEAVE(); + } + + /* + * 6) + */ + + if (size) + { + if ((msg.data = malloc(size)) == NULL) + CORE_ESCAPE("XXX"); + } + else + msg.data = NULL; + + if (size) + { + if (task == _kernel.task) + { + /* + * a) + */ + + memcpy(msg.data, (void*)data, size); + } + else + { + /* + * b) + */ + + if (task_get(task, &otsk) != ERROR_OK) + { + free(msg.data); + + CORE_ESCAPE("XXX"); + } + + if (as_read(otsk->as, data, size, msg.data) != ERROR_OK) + { + free(msg.data); + + CORE_ESCAPE("XXX"); + } + } + } + + msg.as = ID_UNUSED; + msg.size = size; + msg.sender.cell = _kernel.cell; + msg.sender.task = task; + + /* + * 7) + */ + + msg.id = o->id++; + + if (set_push(o->queue, &msg) != ERROR_OK) + { + if (msg.data != NULL) + free(msg.data); + + CORE_ESCAPE("XXX"); + } + + /* + * 8) + */ + + if (machine_call(message, send, task, destination, type, + data, size) != ERROR_OK) + CORE_ESCAPE("XXX"); + + CORE_LEAVE(); +} + +/* + * this function sends a message to a task. the message is delivered + * synchronously and the function returns only when the message is delivered + * correctly. + * + * there is no kernel bufferization. + * + * steps: + * + * 1) check for message destination. + * 2) get message box and task object. + * 3) check size. + * 4) do the transmission. + * a) immediately if a message is being waited. + * b) later if no rendezvous has been established. + * 5) call machine dependent code. + * + */ + +t_error message_transmit(i_task task, + i_node destination, + t_type type, + t_vaddr data, + t_vsize size) +{ + t_id typeid = type; + o_message_type* o; + o_task* otsk; + o_message msg; + o_message* pmsg; + t_setsz setsz; + i_thread thread; + + /* + * 1) + */ + + if (destination.cell != _kernel.cell) + { + /* XXX distr */ + CORE_ESCAPE("XXX"); + } + + /* + * 2) + */ + + if (message_box(destination.task, typeid, &o) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (task_get(task, &otsk) != ERROR_OK) + CORE_ESCAPE("XXX"); + + /* + * 3) + */ + + if (size > o->size) + CORE_ESCAPE("XXX"); + + /* + * 4) + */ + + if (set_size(o->waiters, &setsz) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (setsz != 0) + { + i_node sender; + + /* + * a) + */ + + if (set_pick(o->waiters, (void*)&pmsg) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (size) + if (as_copy(otsk->as, data, pmsg->as, (t_vaddr)pmsg->data, + size) != ERROR_OK) + CORE_ESCAPE("XXX"); + + sender.cell = _kernel.cell; + sender.task = task; + + if (message_return_info(pmsg->blocked, ERROR_OK, size, + sender) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (set_pop(o->waiters) != ERROR_OK) + CORE_ESCAPE("XXX"); + } + else + { + /* + * b) + */ + + if (thread_current(&thread) != ERROR_OK) + CORE_ESCAPE("XXX"); + + msg.as = otsk->as; + msg.data = (void*)data; + msg.size = size; + msg.sender.cell = _kernel.cell; + msg.sender.task = task; + msg.blocked = thread; + + msg.id = o->id++; + + if (set_push(o->queue, &msg) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (thread_block(thread) != ERROR_OK) + CORE_ESCAPE("XXX"); + } + + /* + * 5) + */ + + if (machine_call(message, transmit, task, destination, type, + data, size) != ERROR_OK) + CORE_ESCAPE("XXX"); + + CORE_LEAVE(); +} + +/* + * this function sends a message to a task. the message is delivered + * asynchronously and the function returns immediately. the request parameter + * is used to poll or wait until the buffer is safely copied. + * + * on non-multiprocessor systems, this function is totally equivalent to + * message_send. + * + */ + +t_error message_throw(i_task task, + i_node destination, + t_type type, + t_vaddr data, + t_vsize size, + s_message_request* request) +{ + assert(request != NULL); + + if (cpu_multiprocessor() == ERROR_TRUE) + { + /* XXX smp */ + + CORE_ESCAPE("XXX"); + } + else + { + t_error res; + + res = message_send(task, destination, type, data, size); + + if (res != ERROR_OK) + CORE_ESCAPE("XXX"); + + request->completed = MESSAGE_REQUEST_COMPLETED; + } + + CORE_LEAVE(); +} + +/* + * this function waits and receives a message, whether it is delivered + * asynchronously or synchronously. + * + * steps: + * + * 1) get the message box. + * 2) look for pending message. + * a) no pending message, block. + * b) incoming message, get it. + * 3) call machine code. + * + */ + +t_error message_receive(i_task task, + t_type type, + t_vaddr data, + t_vsize* size, + i_node* sender) +{ + t_id typeid = type; + o_message_type* o; + t_setsz setsz; + i_thread thread; + o_message msg; + o_task* otsk; + + /* + * 1) + */ + + if (message_box(task, typeid, &o) != ERROR_OK) + CORE_ESCAPE("XXX"); + + /* + * 2) + */ + + if (set_size(o->queue, &setsz) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (setsz == 0) + { + /* + * a) + */ + + if (task_get(task, &otsk) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (thread_current(&thread) != ERROR_OK) + CORE_ESCAPE("XXX"); + + msg.id = o->id++; + msg.as = otsk->as; + msg.data = (void*)data; + msg.blocked = thread; + + if (set_push(o->waiters, &msg) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (thread_block(thread) != ERROR_OK) + CORE_ESCAPE("XXX"); + } + else + { + /* + * b) + */ + + if (message_poll(task, type, data, size, sender) != ERROR_OK) + CORE_ESCAPE("XXX"); + + CORE_LEAVE(); + } + + /* + * 3) + */ + + if (machine_call(message, poll, task, type, data, + size, sender) != ERROR_OK) + CORE_ESCAPE("XXX"); + + CORE_LEAVE(); +} + +/* + * this function starts receiving asynchronous messages. the function returns + * immediately, so it is not safe to use the buffer until the request has been + * completely executed. + * + * on non-multiprocessor systems, this function is stricly equivalent to + * message_receive. + * + */ + +t_error message_grab(i_task task, + t_type type, + t_vaddr data, + s_message_request* request) +{ + if (cpu_multiprocessor() == ERROR_TRUE) + { + /* XXX smp */ + + CORE_ESCAPE("XXX"); + } + else + { + t_error res; + t_vsize size; + i_node sender; + + res = message_receive(task, type, data, &size, &sender); + + if (res != ERROR_OK) + CORE_ESCAPE("XXX"); + + request->completed = MESSAGE_REQUEST_COMPLETED; + /* request->size = size; + request->sender = sender;*/ + } + + CORE_LEAVE(); +} + +/* + * this function checks for an incoming message, receives it or returns an + * error immediately. this function can be used with asynchronous ou + * synchronous messages. + * + * steps: + * + * 1) get the destination message box. + * 2) poll for incoming message. + * 3) treat the message. + * a) synchronous case. + * b) asynchronous, intra-kernel case. + * c) asynchronous, inter-address space case. + * 4) call the machine dependent code. + * + */ + +t_error message_poll(i_task task, + t_type type, + t_vaddr data, + t_vsize* size, + i_node* sender) +{ + t_id typeid = type; + o_message_type* o; + o_task* otsk; + o_message* msg; + + /* + * 1) + */ + + if (message_box(task, typeid, &o) != ERROR_OK) + CORE_ESCAPE("XXX"); + + /* + * 2 + */ + + if (set_pick(o->queue, (void*)&msg) != ERROR_OK) + CORE_ESCAPE("XXX"); + + /* + * 3) + */ + + if (msg->as != ID_UNUSED) + { + /* + * a) + */ + + if (msg->size) + { + if (task_get(task, &otsk) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (as_copy(msg->as, (t_vaddr)msg->data, otsk->as, data, + msg->size) != ERROR_OK) + CORE_ESCAPE("XXX"); + } + + if (message_return(msg->blocked, ERROR_OK) != ERROR_OK) + CORE_ESCAPE("XXX"); + } + else + { + if (task == _kernel.task) + { + /* + * b) + */ + + if (msg->size) + memcpy((void*)data, msg->data, msg->size); + } + else + { + /* + * c) + */ + + if (msg->size) + { + if (task_get(task, &otsk) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (as_write(otsk->as, msg->data, msg->size, + data) != ERROR_OK) + CORE_ESCAPE("XXX"); + } + } + + if (msg->data != NULL) + free(msg->data); + } + + *size = msg->size; + *sender = msg->sender; + + if (set_pop(o->queue) != ERROR_OK) + CORE_ESCAPE("XXX"); + + /* + * 4) + */ + + if (machine_call(message, poll, task, type, data, + size, sender) != ERROR_OK) + CORE_ESCAPE("XXX"); + + CORE_LEAVE(); +} + +/* + * this function checks if a message copy (though message_throw or + * message_grab) is finished yet. + * + */ + +t_error message_state(i_task task, + s_message_request request) +{ + /* XXX smp */ + + CORE_ESCAPE("XXX"); +} + +/* + * this function checks if a message copy (though message_throw or + * message_grab) is finished yet and waits for the operation to be completed. + * + */ + +t_error message_wait(i_task task, + s_message_request request, + t_vsize* size, + i_node* sender) +{ + /* XXX smp */ + + CORE_ESCAPE("XXX"); +} + +/* + * this function resumes a blocked thread on a syscall with given return value. + * + */ + +t_error message_return(i_thread thread, + t_error code) +{ + if (machine_call(message, return, thread, code) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (thread_start(thread) != ERROR_OK) + CORE_ESCAPE("XXX"); + + CORE_LEAVE(); +} + +/* + * this function resumes a blocked thread on a syscall with given info. + * + */ + +t_error message_return_info(i_thread thread, + t_error code, + t_vsize size, + i_node sender) +{ + if (machine_call(message, return_info, thread, code, size, + sender) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (thread_start(thread) != ERROR_OK) + CORE_ESCAPE("XXX"); + + CORE_LEAVE(); +} + + +/* + * this function initializes the message manager. + * + * steps: + * + * 1) allocate some memory for the manager structure. + * 2) create message types for the kernel task. + * 3) call the machine dependent code. + * + */ + +t_error message_initialize(void) +{ + /* + * XXX + */ + + module_call(console, message, + '+', "initializing the message manager\n"); + + /* + * 1) + */ + + memset(&_message, 0x0, sizeof (m_message)); + + /* + * 2) + */ + + if (message_register(_kernel.task, MESSAGE_TYPE_INTERFACE, + sizeof (o_syscall)) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (message_register(_kernel.task, MESSAGE_TYPE_EVENT, + sizeof (o_event_message)) != ERROR_OK) + CORE_ESCAPE("XXX"); + + if (message_register(_kernel.task, MESSAGE_TYPE_TIMER, + sizeof (o_timer_message)) != ERROR_OK) + CORE_ESCAPE("XXX"); + + /* + * 3) + */ + + if (machine_call(message, initialize) != ERROR_OK) + CORE_ESCAPE("XXX"); + + CORE_LEAVE(); +} + +/* + * this function cleans the message manager. + * + * steps: + * + * 1) call the dependent code. + * 3) free kernel message types. + * 2) free the manager structure. + * + */ + +t_error message_clean(void) +{ + /* + * XXX + */ + + module_call(console, message, + '+', "cleaning the message manager\n"); + + /* + * 1) + */ + + if (machine_call(message, clean) != ERROR_OK) + CORE_ESCAPE("XXX"); + + /* + * 2) + */ + + if (message_flush(_kernel.task) != ERROR_OK) + CORE_ESCAPE("XXX"); + + CORE_LEAVE(); +} diff --git a/kaneton/core/region/Makefile b/kaneton/core/region/Makefile new file mode 100644 index 0000000..b41b16a --- /dev/null +++ b/kaneton/core/region/Makefile @@ -0,0 +1,76 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton.STABLE/kaneton/core/region/Makefile +# +# created julien quintard [sun jun 10 17:00:15 2007] +# updated julien quintard [sun nov 28 19:44:38 2010] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/core/region + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +REGION_C := region-fit.c + +REGION_O := $(REGION_C:.c=.o) + +REGION_INCLUDE := $(_CORE_INCLUDE_DIR_)/region.h + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(_REGION_LO_) + +$(_REGION_LO_): $(REGION_O) + $(call env_remove,$(_REGION_LO_),) + + $(call env_archive,$(_REGION_LO_),$(REGION_O),) + +clear: + $(call env_remove,$(REGION_O),) + + $(call env_remove,$(_REGION_LO_),) + + $(call env_purge,) + +prototypes: + $(call env_prototypes,$(REGION_INCLUDE),) + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(REGION_C),) + +dependencies: + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/core/region/region-fit.c b/kaneton/core/region/region-fit.c new file mode 100644 index 0000000..acb3273 --- /dev/null +++ b/kaneton/core/region/region-fit.c @@ -0,0 +1,1627 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/region/region-fit.c + * + * created julien quintard [wed nov 23 09:19:43 2005] + * updated julien quintard [fri apr 8 09:28:15 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * the region manager provides functionalities for mapping segments through + * so called 'regions'. therefore the terms "mapping" and "reserving a + * region" can be interchangeably used. + * + * unlike segments, regions live in an address space. therefore a region + * itself is meanginless outside its address space. besides, there is no + * global set of regions as there is for segments. instead, the address + * space maintains a set containing its region objects. + */ + +/* + * ---------- algorithm ------------------------------------------------------- + * + * this implementation has a specificity regarding region identifiers. indeed, + * like for segments, region identifiers are based on the object's address. + * a region's identifier represents the page number in the virtual space, + * starting at one. in other words: id = address / page size. regions + * are identified this way in order to ensure that the regions are always + * sorted in their set. + * + * this implementation relies on the fit algorithm though this algorithm can + * be further configured through the REGION_FIT macro: + * + * o FIT_FIRST: first fit algorithm - the first large enough space is used. + * o FIT_BEST: best fit algorithm - the smaller space is used. + * o etc. + */ + +#if (REGION_ALGORITHM == REGION_ALGORITHM_FIT) + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * include the machine-specific definitions required by the core. + */ + +machine_include(region); + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the region manager structure. + */ + +m_region _region; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function shows a region's attributes. + * + * steps: + * + * 1) retrieve the region object. + * 2) build the options string. + * 3) display the attributes. + * 4) call the machine. + */ + +t_error region_show(i_as asid, + i_region regid, + mt_margin margin) +{ + o_region* o; + char options[2]; + + /* + * 1) + */ + + if (region_get(asid, regid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + /* + * 2) + */ + + memset(options, 0x0, sizeof (options)); + + if (o->options & REGION_OPTION_FORCE) + options[0] = 'f'; + else + options[0] = '.'; + + options[1] = '\0'; + + /* + * 3) + */ + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + "region:\n", + MODULE_CONSOLE_MARGIN_VALUE(margin)); + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " core: id(%qd) range(0x%08x - 0x%08x) segment(%qd) " + "offset(0x%x) size(0x%x) options(%s)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + o->id, + o->address, + o->address + o->size - 1, + o->segment, + o->offset, + o->size, + options); + + /* + * 3) + */ + + if (machine_call(region, show, asid, regid, margin) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function dumps all the regions of the given address space. + * + * steps: + * + * 1) display general information. + * 2) display the content of the vault. + * 3) call the machine. + */ + +t_error region_dump(void) +{ + t_uint32 i; + + /* + * 1) + */ + + module_call(console, message, + '#', "region manager:\n"); + + module_call(console, message, + '#', " core: base(0x%08x) size(0x%08x)\n", + _region.base, + _region.size); + + /* + * 2) + */ + + module_call(console, message, + '#', " vault: size(%u)\n", + REGION_VAULT_SIZE); + + for (i = 0; i < REGION_VAULT_SIZE; i++) + { + if (_region.vault[i].state == REGION_VAULT_STATE_AVAILABLE) + continue; + + module_call(console, message, + '#', " core: as(%qd) address(0x%08x) size(0x%x)\n", + _region.vault[i].as, + _region.vault[i].address, + _region.vault[i].size); + } + + /* + * 3) + */ + + if (machine_call(region, dump) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function marks an area of virtual memory as being temporarily + * unreservable. + * + * this functionality has been introduced in order to counter the following + * scenario. + * + * let us recall that the function region_inject() calls set_add(). should + * set_add() need to expand the array, malloc() would be called which, may + * it need extra memory, would call map_reserve() and, in turn, + * region_reserve() and region_space(). region_space() could then return + * the address of the memory area that the caller was trying to inject in + * order to secure it. + * + * the following function can therefore be used to mark a region in order + * to render it unreservable until enough memory is allocated to the set + * manager to handle an additional element. + * + * steps: + * + * 1) go through the vault entries. + * a) if the entry is available, stop looking for one. + * 2) if no available entry has been found, return a fatal error. + * 3) register the given zone and mark the entry as being used. + * 4) call the machine. + */ + +t_error region_protect(i_as as, + t_vaddr address, + t_vsize size) +{ + t_uint32 i; + + /* + * 1) + */ + + for (i = 0; i < REGION_VAULT_SIZE; i++) + { + /* + * a) + */ + + if (_region.vault[i].state == REGION_VAULT_STATE_AVAILABLE) + break; + } + + /* + * 2) + */ + + if (i == REGION_VAULT_SIZE) + CORE_ESCAPE("unable to protect this zone as the vault is already full"); + + /* + * 3) + */ + + _region.vault[i].state = REGION_VAULT_STATE_USED; + _region.vault[i].as = as; + _region.vault[i].address = address; + _region.vault[i].size = size; + + /* + * 4) + */ + + if (machine_call(region, protect, as, address, size) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function performs the opposite action of region_protect() i.e + * it unmarks a memory area. + * + * this function is usally called after the region has been successfully + * registered in the manager so that it no longer needs to be protected + * against reservation. + * + * steps: + * + * 1) go through the vault entries. + * a) if the zone to unprotect is found, stop. + * 2) if the zone has not been located, return an error. + * 3) mark the entry as available. + * 4) call the machine. + */ + +t_error region_unprotect(i_as as, + t_vaddr address, + t_vsize size) +{ + t_uint32 i; + + /* + * 1) + */ + + for (i = 0; i < REGION_VAULT_SIZE; i++) + { + /* + * a) + */ + + if ((_region.vault[i].state == REGION_VAULT_STATE_USED) && + (_region.vault[i].as == as) && + (_region.vault[i].address == address) && + (_region.vault[i].size == size)) + break; + } + + /* + * 2) + */ + + if (i == REGION_VAULT_SIZE) + CORE_ESCAPE("unable to locate the zone to unprotect"); + + /* + * 3) + */ + + memset(&_region.vault[i], 0x0, sizeof (s_region_zone)); + + /* + * 4) + */ + + if (machine_call(region, unprotect, as, address, size) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function returns true if the given address lies in a protected + * zone, false otherwise. + * + * steps: + * + * 1) go through the vault entries. + * a) if the given address lies in the entry's zone and matches the + * address space, return true. + */ + +t_error region_protected(i_as as, + t_vaddr address) +{ + t_uint32 i; + + /* + * 1) + */ + + for (i = 0; i < REGION_VAULT_SIZE; i++) + { + /* + * a) + */ + + if ((_region.vault[i].state == REGION_VAULT_STATE_USED) && + (_region.vault[i].as == as) && + (_region.vault[i].address <= address) && + (address < (_region.vault[i].address + _region.vault[i].size))) + CORE_TRUE(); + } + + CORE_FALSE(); +} + +/* + * this function tries to find free space in the region set via the + * first fit algorithm. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the address space object. + * 2) if there is no region yet... + * a) verify that the whole region space is large enough to satisfy the + * request. + * b) try every address to make sure it is not protected. + * c) if no address has been found, return an error. + * 3) otherwise, retrieve the very first region object. + * 4) perhaps there is enough space between the beginning of the virtual + * space and the first region's address. + * 5) go through the regions of the address space. + * a) retrieve the region object. + * b) try to retrieve the following region object, or escape otherwise. + * c) if there is space between the two regions, return this address. + * 6) retrieve the last region. + * 7) try to find space between the last region and the end of the virtual + * space. + */ + +t_error region_fit_first(i_as asid, + t_vsize size, + t_vaddr* address) +{ + o_region* current; + t_state state; + o_region* head; + o_region* tail; + o_as* as; + s_iterator i; + + /* + * 0) + */ + + if (size == 0) + CORE_ESCAPE("unable to find space for zero bytes"); + + if (address == NULL) + CORE_ESCAPE("the 'address' argument is null"); + + if ((size % ___kaneton$pagesz) != 0) + CORE_ESCAPE("the size is not aligned on the machine's page size: '%u'", + ___kaneton$pagesz); + + /* + * 1) + */ + + if (as_get(asid, &as) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the address space object"); + + /* + * 2) + */ + + if (set_head(as->regions, &i) != ERROR_TRUE) + { + /* + * a) + */ + + if ((_region.size - _region.base) < size) + CORE_ESCAPE("there is not enough memory to satisfy the reservation " + "request"); + + /* + * b) + */ + + for (*address = _region.base; + *address < (_region.base + _region.size); + *address = *address + ___kaneton$pagesz) + { + /* + * i) + */ + + if (region_protected(as->id, *address) == ERROR_FALSE) + CORE_LEAVE(); + } + + /* + * c) + */ + + CORE_ESCAPE("unable to find available memory"); + } + + /* + * 3) + */ + + if (set_object(as->regions, i, (void**)&head) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the address space's very first region"); + + /* + * 4) + */ + + for (*address = _region.base; + (*address + size) <= head->address; + *address = *address + ___kaneton$pagesz) + { + if (region_protected(as->id, *address) == ERROR_FALSE) + CORE_LEAVE(); + } + + /* + * 5) + */ + + set_foreach(SET_OPTION_FORWARD, as->regions, &i, state) + { + o_region* next; + s_iterator j; + + /* + * a) + */ + + if (set_object(as->regions, i, (void**)¤t) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + /* + * b) + */ + + if (set_next(as->regions, i, &j) != ERROR_TRUE) + break; + + if (set_object(as->regions, j, (void**)&next) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the next region object"); + + /* + * c) + */ + + for (*address = current->address + current->size; + (*address + size) <= next->address; + *address = *address + ___kaneton$pagesz) + { + if (region_protected(as->id, *address) == ERROR_FALSE) + CORE_LEAVE(); + } + } + + /* + * 6) + */ + + if (set_tail(as->regions, &i) != ERROR_TRUE) + CORE_ESCAPE("unable to locate the address space's last region"); + + if (set_object(as->regions, i, (void**)&tail) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + /* + * 7) + */ + + for (*address = tail->address + tail->size; + (*address + size) <= (_region.base + _region.size); + *address = *address + ___kaneton$pagesz) + { + if (region_protected(as->id, *address) == ERROR_FALSE) + CORE_LEAVE(); + } + + CORE_ESCAPE("unable to locate enough space between the existing regions " + "to satisfy the reservation request"); +} + +/* + * this function tries to find available space in the address space. + * + * note that this function is a wrapper over the several fit algorithms + * provided. + * + * steps: + * + * 0) verify the arguments. + * 1) forward the call to the right algorithm. + */ + +t_error region_space(i_as asid, + t_vsize size, + t_vaddr* address) +{ + /* + * 0) + */ + + if (size == 0) + CORE_ESCAPE("unable to find space for zero bytes"); + + if (address == NULL) + CORE_ESCAPE("the 'address' argument is null"); + + if ((size % ___kaneton$pagesz) != 0) + CORE_ESCAPE("the size is not aligned on the machine's page size: '%u'", + ___kaneton$pagesz); + + /* + * 1) + */ + + switch (REGION_FIT) + { + case FIT_FIRST: + { + if (region_fit_first(asid, size, address) != ERROR_OK) + CORE_ESCAPE("unable to locate space through the first-fit " + "algorithm"); + + break; + } + default: + CORE_ESCAPE("unknown region's fit algorithm '%u'", + REGION_FIT); + } + + CORE_LEAVE(); +} + +/* + * this function injects a region object into the address space. + * + * note that the object must already be ready for insertion in the set + * of regions. therefore if the set has not be configured to clone the + * objects to add, the object should have been pre-allocated. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the address space object. + * 2) fill the remaining region attributes, especially the identifier. + * 3) protect the zone so that it cannot get reserved until it has been + * properly added to the set. + * 4) add the object to the address space's set of regions. + * 5) unprotect the zone. + * 6) call the machine. + */ + +t_error region_inject(i_as as, + o_region* object, + i_region* id) +{ + o_as* o; + + /* + * 0) + */ + + if (object == NULL) + CORE_ESCAPE("the 'object' argument is null"); + + if (id == NULL) + CORE_ESCAPE("the 'id' argument is null"); + + if (object->options & REGION_OPTION_INVALID) + CORE_ESCAPE("the object's options are invalid"); + + if (object->size == 0) + CORE_ESCAPE("the object's size is zero"); + + if ((object->size % ___kaneton$pagesz) != 0) + CORE_ESCAPE("the size is not aligned on the machine's page size: '%u'", + ___kaneton$pagesz); + + /* + * 1) + */ + + if (as_get(as, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the address space object"); + + /* + * 2) + */ + + object->id = REGION_IDENTIFIER(object); + object->options |= REGION_OPTION_FORCE; + + *id = object->id; + + /* + * 3) + */ + + if (region_protect(o->id, object->address, object->size) != ERROR_OK) + CORE_ESCAPE("unable to protect the zone"); + + /* + * 4) + */ + + if (set_add(o->regions, object) != ERROR_OK) + CORE_ESCAPE("unable to add the object to the address space's set of " + "regions"); + + /* + * 5) + */ + + if (region_unprotect(o->id, object->address, object->size) != ERROR_OK) + CORE_ESCAPE("unable to unprotect the zone"); + + /* + * 6) + */ + + if (machine_call(region, inject, as, object, id) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function splits a region into two regions according to the + * splitting size. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the region object and its associated segment. + * 2) verify that the the splitting size fits the region. + * 3) build the new future-'right' region. + * 4) inject the new region in the address space's set of regions. + * 5) adjust the old future-'left' region size. + * 6) set the new left and right identifiers. + * 7) call the machine. + */ + +t_error region_split(i_as asid, + i_region regid, + t_vsize size, + i_region* left, + i_region* right) +{ + i_region useless; + o_region* reg; + o_segment* seg; + o_region* new; + + /* + * 0) + */ + + if (size == 0) + CORE_ESCAPE("unable to split to a size of zero"); + + if (left == NULL) + CORE_ESCAPE("the 'left' argument is null"); + + if (right == NULL) + CORE_ESCAPE("the 'right' argument is null"); + + if ((size % ___kaneton$pagesz) != 0) + CORE_ESCAPE("the size is not aligned on the machine's page size: '%u'", + ___kaneton$pagesz); + + /* + * 1) + */ + + if (region_get(asid, regid, ®) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + if (segment_get(reg->segment, &seg) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the segment object"); + + /* + * 2) + */ + + if (reg->size <= size) + CORE_ESCAPE("the splitted size is larger than the current region's size"); + + /* + * 3) + */ + + if ((new = malloc(sizeof (o_region))) == NULL) + CORE_ESCAPE("unable to allocate memory for the region object"); + + new->segment = reg->segment; + new->offset = reg->offset + size; + new->address = reg->address + size; + new->options = reg->options; + new->size = reg->size - size; + + new->id = REGION_IDENTIFIER(new); + + /* + * 4) + */ + + if (region_inject(asid, new, &useless) != ERROR_OK) + CORE_ESCAPE("unable to inject the object in the address space's " + "set of regions"); + + /* + * 5) + */ + + reg->size = size; + + /* + * 6 + */ + + *left = reg->id; + *right = new->id; + + /* + * 7) + */ + + if (machine_call(region, split, + asid, + regid, + size, + left, + right) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function resizes a region according to the given size which can be + * smaller or larger than the current size. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the address space object. + * 2) retrieve the region object to resize. + * 3) set the future identifier since it will always be the same. + * 4) resize the region depending on the future size. + * A) if the future size is smaller than the current size... + * a) call the machine. + * B) if the future size is larger than the current size... + * a) try to locate the following region. + * i) if such a region exist... + * #1) retrieve the following region object. + * #2) verify that there is enough space between the current region + * and the following one to resize i.e resizing will not make + * the current region overlap the following one. + * ii) if the current region is the last one... + * #1) verify that there is enough space between the current region + * and the end of the virtual space to accept the resizing. + * b) retrieve the segment object mapped by the region. + * c) verify that the region's future size will not be larger than the + * segment's. + * d) protect the expanded part of the future region. + * e) call the machine. + * f) unprotect the zone. + * C) if the future size is identical to the current one... + * a) nothing to do in this case. + * 5) update the region's size with the resized size. + */ + +t_error region_resize(i_as as, + i_region old, + t_vsize size, + i_region* new) +{ + + o_region* reg = NULL; + o_region* next_reg; + o_segment* seg; + o_as* oas; + s_iterator i; + s_iterator next; + + /* + * 0) + */ + + if (size == 0) + CORE_ESCAPE("unable to resize to a size of zero"); + + if (new == NULL) + CORE_ESCAPE("the 'new' argument is null"); + + if ((size % ___kaneton$pagesz) != 0) + CORE_ESCAPE("the size is not aligned on the machine's page size: '%u'", + ___kaneton$pagesz); + + /* + * 1) + */ + + if (as_get(as, &oas) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the address space object"); + + /* + * 2) + */ + + if (set_locate(oas->regions, old, &i) != ERROR_OK) + CORE_ESCAPE("unable to locate the object in the address space's " + "set of regions"); + + if (set_object(oas->regions, i, (void**)®) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + /* + * 3) + */ + + *new = old; + + /* + * 4) + */ + + if (size < reg->size) + { + /* + * A) + */ + + /* + * a) + */ + + if (machine_call(region, resize, + as, + old, + size, + new) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + } + else if (size > reg->size) + { + /* + * B) + */ + + /* + * a) + */ + + if (set_next(oas->regions, i, &next) == ERROR_TRUE) + { + /* + * i) + */ + + /* + * #1) + */ + + if (set_object(oas->regions, next, (void**)&next_reg) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the next region object"); + + /* + * #2) + */ + + if ((reg->address + size) >= next_reg->address) + CORE_ESCAPE("unable to resize the region since it would " + "overlap with the next region"); + } + else + { + /* + * ii) + */ + + /* + * #1) + */ + + if ((reg->address + size) >= (_region.base + _region.size)) + CORE_ESCAPE("unable to resize the region since it would reach the " + "end of the available region space"); + } + + /* + * b) + */ + + if (segment_get(reg->segment, &seg) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the segment object"); + + /* + * c) + */ + + if (size > seg->size) + CORE_ESCAPE("unable to resize a region to a size larger than " + "its segment"); + + /* + * d) + */ + + if (region_protect(as, + reg->address + reg->size, + size - reg->size) != ERROR_OK) + CORE_ESCAPE("unable to protect the zone"); + + /* + * e) + */ + + if (machine_call(region, resize, + as, + old, + size, + new) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * f) + */ + + if (region_unprotect(as, + reg->address + reg->size, + size - reg->size) != ERROR_OK) + CORE_ESCAPE("unable to unprotect the zone"); + } + else + { + /* + * C) + */ + + /* + * a) + */ + } + + /* + * 5) + */ + + reg->size = size; + + CORE_LEAVE(); +} + +/* + * this function merges two adjacent regions into a single one. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve both region objects. + * 2) verify that the regions are adjacents but also that they have + * the same privileges. + * 3) retrieve the address space object. + * 4) set the final region size. + * 5) remove the 'right' region from the address space's set of regions. + * 6) call the machine. + * 7) return the new region identifier. + */ + +t_error region_coalesce(i_as asid, + i_region left, + i_region right, + i_region* region) +{ + o_as* as; + o_region* oleft; + o_region* oright; + + /* + * 0) + */ + + if (left == right) + CORE_ESCAPE("unable to coalesce the two same regions"); + + if (region == NULL) + CORE_ESCAPE("the 'region' argument is null"); + + /* + * 1) + */ + + if (region_get(asid, left, &oleft) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + if (region_get(asid, right, &oright) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + /* + * 2) + */ + + if (oleft->address + oleft->size != oright->address || + oleft->segment != oright->segment || + oleft->offset + oleft->size != oright->offset) + CORE_ESCAPE("unable to coalesce non-adjacent regions"); + + /* + * 3) + */ + + if (as_get(asid, &as) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the address space object"); + + /* + * 4) + */ + + oleft->size += oright->size; + + /* + * 5) + */ + + if (set_remove(as->regions, right) != ERROR_OK) + CORE_ESCAPE("unable to remove the object from the address space's set " + "of regions"); + + /* + * 6) + */ + + if (machine_call(region, coalesce, + asid, + left, + right, + region) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 7) + */ + + *region = left; + + CORE_LEAVE(); +} + +/* + * this function reserves a region in order to map the given segment + * at a precise offset, for a specific size and according to several options. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the address space object. + * 2) retrieve the segment object. + * 3) verify that the region does not map outside the segment. + * 4) compute the future region's address depending on the options. + * A) if the region has been request to be located at a precise address... + * a) verify that the address lies in the virtual space. + * b) go through the existing regions and verify that this address is + * not already in use. + * B) if the region can be reserved anywhere... + * a) look for available space in the address space's set of regions. + * 5) protect the located zone. + * 6) allocate the object. + * 7) fill the object's attributes. + * 8) add the object to the address space's set of regions. + * 9) unprotect the zone. + * 10) call the machine. + * 11) return the region's identifier. + */ + +t_error region_reserve(i_as asid, + i_segment segid, + t_paddr offset, + t_options options, + t_vaddr address, + t_vsize size, + i_region* region) +{ + o_segment* segment; + o_as* as; + o_region* o; + t_state state; + s_iterator i; + o_region* data; + + /* + * 0) + */ + + if (options & REGION_OPTION_INVALID) + CORE_ESCAPE("the options seem to be invalid"); + + if (size == 0) + CORE_ESCAPE("unable to reserve a region with a zero size"); + + if (region == NULL) + CORE_ESCAPE("the 'region' argument is null"); + + if ((size % ___kaneton$pagesz) != 0) + CORE_ESCAPE("the size is not aligned on the machine's page size: '%u'", + ___kaneton$pagesz); + + /* + * 1) + */ + + if (as_get(asid, &as) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the address space object"); + + /* + * 2) + */ + + if (segment_get(segid, &segment) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the segment object"); + + /* + * 3) + */ + + if (segment->size < (offset + size)) + CORE_ESCAPE("out-of-bound operation"); + + /* + * 4) + */ + + if (options & REGION_OPTION_FORCE) + { + /* + * A) + */ + + /* + * a) + */ + + if ((address < _region.base) || + (address >= (_region.base + _region.size))) + CORE_ESCAPE("the provided address is either too low or too high"); + + /* + * b) + */ + + set_foreach(SET_OPTION_FORWARD, as->regions, &i, state) + { + if (set_object(as->regions, i, (void**)&data) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + if (data->address <= address && data->address + data->size > address) + CORE_ESCAPE("unable to reserve the region at this precise " + "location 0x%x: seems to be already taken", + address); + } + } + else + { + /* + * B) + */ + + /* + * a) + */ + + if (region_space(asid, size, &address) != ERROR_OK) + CORE_ESCAPE("unable to find available space for the region " + "reservation"); + } + + /* + * 5) + */ + + if (region_protect(asid, address, size) != ERROR_OK) + CORE_ESCAPE("unable to protect the zone"); + + /* + * 6) + */ + + if ((o = malloc(sizeof (o_region))) == NULL) + CORE_ESCAPE("unable to allocate memory for the region object"); + + /* + * 7) + */ + + o->address = address; + o->id = REGION_IDENTIFIER(o); + o->segment = segid; + o->size = size; + o->offset = offset; + o->options = options; + + /* + * 8) + */ + + if (set_add(as->regions, o) != ERROR_OK) + CORE_ESCAPE("unable to add the object to the address space's set of " + "regions"); + + /* + * 9) + */ + + if (region_unprotect(asid, address, size) != ERROR_OK) + CORE_ESCAPE("unable to unprotect the zone"); + + /* + * 10) + */ + + if (machine_call(region, reserve, + asid, segid, offset, options, + address, size, region) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 11) + */ + + *region = o->id; + + CORE_LEAVE(); +} + +/* + * this function releases an existing region from the given address space. + * + * steps: + * + * 1) call the machine. + * 2) retrieve the address space object. + * 3) retrieve the region object. + * 4) remove the region from the address space's set of regions. + */ + +t_error region_release(i_as asid, + i_region regid) +{ + o_as* as; + + /* + * 1) + */ + + if (machine_call(region, release, asid, regid) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 2) + */ + + if (as_get(asid, &as) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the address space object"); + + /* + * 3) + */ + + if (set_remove(as->regions, regid) != ERROR_OK) + CORE_ESCAPE("unable to remove the object from the address space's " + "set of regions"); + + CORE_LEAVE(); +} + +/* + * this function locates the region in which the given address lies. + * + * note that this function returns either true or false depending on the + * success of the look up. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the address space object. + * 2) go through the regions. + * a) retrieve the region object. + * b) check that the address resides in this region. + * 3) if no region has been found, return false; + */ + +t_error region_locate(i_as as, + t_vaddr address, + i_region* id) +{ + o_as* o; + t_state state; + s_iterator i; + + /* + * 0) + */ + + assert(id != NULL); + + /* + * 1) + */ + + assert(as_get(as, &o) == ERROR_OK); + + /* + * 2) + */ + + set_foreach(SET_OPTION_FORWARD, o->regions, &i, state) + { + o_region* r; + + /* + * a) + */ + + assert(set_object(o->regions, i, (void**)&r) == ERROR_OK); + + /* + * b) + */ + + if ((r->address <= address) && (address <= (r->address + r->size - 1))) + { + *id = r->id; + + CORE_TRUE(); + } + } + + /* + * 3) + */ + + CORE_FALSE(); +} + +/* + * this function removes every region that belongs to the give address space. + * + * steps: + * + * 1) call the machine. + * 2) retrieve the address space object. + * 3) go through the existing regions and release them all. + */ + +t_error region_flush(i_as asid) +{ + o_as* as; + s_iterator it; + i_region* obj; + + /* + * 1) + */ + + if (machine_call(region, flush, asid) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 2) + */ + + if (as_get(asid, &as) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the address space object"); + + /* + * 3) + */ + + while (set_head(as->regions, &it) == ERROR_TRUE) + { + if (set_object(as->regions, it, (void**)&obj) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region object"); + + if (region_release(asid, *obj) != ERROR_OK) + CORE_ESCAPE("unable to release the region"); + } + + CORE_LEAVE(); +} + +/* + * this function returns true if the region object exists. + * + * steps: + * + * 0) verify the arguments. + * 1) check if the region identifier can be located in the address space's + * set of regions. + */ + +t_error region_exist(i_as asid, + i_region regid) +{ + o_as* as; + + /* + * 0) + */ + + assert(as_get(asid, &as) == ERROR_OK); + + /* + * 1) + */ + + if (set_exist(as->regions, regid) != ERROR_TRUE) + CORE_FALSE(); + + CORE_TRUE(); +} + +/* + * this function returns a region object from the given address space. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the address space object. + * 2) retrieve the object from the address space's set of regions. + */ + +t_error region_get(i_as asid, + i_region regid, + o_region** object) +{ + o_as* as; + + /* + * 0) + */ + + if (object == NULL) + CORE_ESCAPE("the 'object' argument is null"); + + /* + * 1) + */ + + if (as_get(asid, &as) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the address space object"); + + /* + * 2) + */ + + if (set_get(as->regions, regid, (void**)object) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the region from the address space's set " + "of regions"); + + CORE_LEAVE(); +} + +/* + * this function initializes the region manager. + * + * the _base_ and _size_ arguments represents the virtual space i.e its + * base address and available size. + * + * steps: + * + * 0) verify the arguments. + * 1) display a message. + * 2) initialize the region manager's structure. + * 3) set the base and size attributes. note that the base address is + * incremented by the page size. this is done in order to render the + * first page unuseable. this has the impact to produce a page fault + * every time the first page is referenced. this is a common practice + * since most pointers are manually initialised by programmers to NULL. + * 4) calls the machine. + */ + +t_error region_initialize(t_vaddr base, + t_vsize size) +{ + /* + * 0) + */ + + if (size == 0) + CORE_ESCAPE("unable to initialize the region manager with a size of zero"); + + if ((base % ___kaneton$pagesz) != 0) + CORE_ESCAPE("the base is not aligned on the machine's page size: '%u'", + ___kaneton$pagesz); + + if ((size % ___kaneton$pagesz) != 0) + CORE_ESCAPE("the size is not aligned on the machine's page size: '%u'", + ___kaneton$pagesz); + + /* + * 1) + */ + + module_call(console, message, + '+', "initializing the region manager\n"); + + /* + * 2) + */ + + memset(&_region, 0x0, sizeof (m_region)); + + /* + * 3) + */ + + _region.base = base + ___kaneton$pagesz; + _region.size = size - ___kaneton$pagesz; + + /* + * 4) + */ + + if (machine_call(region, initialize, base, size) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function cleans the region manager. + * + * steps: + * + * 1) display a message. + * 2) call the machine. + */ + +t_error region_clean(void) +{ + /* + * 1) + */ + + module_call(console, message, + '+', "cleaning the region manager\n"); + + /* + * 2) + */ + + if (machine_call(region, clean) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +#endif diff --git a/kaneton/core/scheduler/Makefile b/kaneton/core/scheduler/Makefile new file mode 100644 index 0000000..34311bc --- /dev/null +++ b/kaneton/core/scheduler/Makefile @@ -0,0 +1,76 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton.STABLE/kaneton/core/scheduler/Makefile +# +# created julien quintard [thu jun 28 20:35:48 2007] +# updated julien quintard [sun nov 28 19:44:50 2010] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/core/scheduler + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +SCHEDULER_C := scheduler-mfq.c + +SCHEDULER_O := $(SCHEDULER_C:.c=.o) + +SCHEDULER_INCLUDE := $(_CORE_INCLUDE_DIR_)/scheduler.h + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(_SCHEDULER_LO_) + +$(_SCHEDULER_LO_): $(SCHEDULER_O) + $(call env_remove,$(_SCHEDULER_LO_),) + + $(call env_archive,$(_SCHEDULER_LO_),$(SCHEDULER_O),) + +clear: + $(call env_remove,$(SCHEDULER_O),) + + $(call env_remove,$(_SCHEDULER_LO_),) + + $(call env_purge,) + +prototypes: + $(call env_prototypes,$(SCHEDULER_INCLUDE),) + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(SCHEDULER_C),) + +dependencies: + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/core/scheduler/scheduler-mfq.c b/kaneton/core/scheduler/scheduler-mfq.c new file mode 100644 index 0000000..b8fdf4e --- /dev/null +++ b/kaneton/core/scheduler/scheduler-mfq.c @@ -0,0 +1,463 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/Down...n/kaneton/core/scheduler/scheduler-mfq.c + * + * created matthieu bucchianeri [sat jun 3 22:36:59 2006] + * updated julien quintard [mon apr 11 13:23:09 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * the scheduler manager provides functionalities for managing the + * execution of the tasks and their threads on the possibly multiple CPUs + * of the computer. + * + * note that tasks are not scheduled. indeed, the active entity is the + * thread. therefore, adding a thread to the scheduler makes it a candidate + * for future election. + * + * the core function is the scheduler_elect() function which chooses + * the next thread to execute, though the current thread may be selected + * to continue its execution. + */ + +#if (SCHEDULER_ALGORITHM == SCHEDULER_ALGORITHM_MFQ) + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * include the machine-specific definitions required by the core. + */ + +machine_include(scheduler); + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the kernel manager. + */ + +extern m_kernel _kernel; + +/* + * the cpu manager. + */ + +extern m_cpu _cpu; + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the scheduler manager. + */ + +m_scheduler _scheduler; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function starts the given scheduler. + * + * steps: + * + * 1) retrieve the scheduler object. + * 2) change the scheduler's state. + * 3) call the machine. + */ + +t_error scheduler_start(i_cpu id) +{ + o_scheduler* scheduler; + + /* + * 1) + */ + + if (scheduler_get(id, &scheduler) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the scheduler object"); + + /* + * 2) + */ + + scheduler->state = SCHEDULER_STATE_START; + + /* + * 3) + */ + + if (machine_call(scheduler, start, id) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function stops the given scheduler. + * + * steps: + * + * 1) retrieve the scheduler object. + * 2) change the scheduler's state. + * 3) call the machine. + */ + +t_error scheduler_stop(i_cpu id) +{ + o_scheduler* scheduler; + + /* + * 1) + */ + + if (scheduler_get(id, &scheduler) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the scheduler object"); + + /* + * 2) + */ + + scheduler->state = SCHEDULER_STATE_STOP; + + /* + * 3) + */ + + if (machine_call(scheduler, stop, id) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function modifies the quantum. + */ + +t_error scheduler_quantum(t_quantum quantum) +{ + /* FIXME[code to complete] */ + + CORE_LEAVE(); +} + +/* + * this function enables the current thread to voluntarily relinquish its + * execution, hence permitting another thread to be scheduled immediately + * on this CPU. + */ + +t_error scheduler_yield(void) +{ + /* FIXME[code to complete] */ + + CORE_LEAVE(); +} + +/* + * this function elects the future thread to execute, taking care to + * save the currently executing one, should both the task and thread be + * still in a running state. + * + * note that this function may elect the already running thread if + * (i) it has not expired and (ii) there is no thread with a higher priority + * in the active list. + * + * finally, this function may detect that the scheduler has been stopped. + * should this occur, the elected thread is saved and the original kernel + * thread is specially scheduled, hence returning to its initial state. + */ + +t_error scheduler_elect(void) +{ + /* FIXME[code to complete] */ + + CORE_LEAVE(); +} + +/* + * this function adds a thread to the scheduler. + */ + +t_error scheduler_add(i_thread id) +{ + /* FIXME[code to complete] */ + + CORE_LEAVE(); +} + +/* + * this function removes a thread from the scheduler. + */ + +t_error scheduler_remove(i_thread id) +{ + /* FIXME[code to complete] */ + + CORE_LEAVE(); +} + +/* + * this function updates a thread from the scheduler after its priority + * has changed. + * + * note that the easiest way could seem to be to remove then add the + * thread, in which case it would be added to its proper queue depending + * on its new priority. unfortunately this solution would make the thread + * lose its remaining timeslices. besides, if the thread to remove is the + * currently scheduled thread, removing it would incurr a yield. in this + * case, the thread would no longer be scheduled and would therefore not + * have the chance to add itself back to the scheduler. + */ + +t_error scheduler_update(i_thread id) +{ + /* FIXME[code to complete] */ + + CORE_LEAVE(); +} + +/* + * this function returns true if the scheduler for the given CPU identifier + * exists. + */ + +t_error scheduler_exist(i_cpu id) +{ + if (set_exist(_scheduler.schedulers, id) != ERROR_TRUE) + CORE_FALSE(); + + CORE_TRUE(); +} + +/* + * this function retrives the scheduler for the given CPU. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the object from the set of schedulers. + */ + +t_error scheduler_get(i_cpu id, + o_scheduler** object) +{ + /* + * 0) + */ + + if (object == NULL) + CORE_ESCAPE("the 'object' argument is null"); + + /* + * 1) + */ + + if (set_get(_scheduler.schedulers, id, (void**)object) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the object from the set of schedulers"); + + CORE_LEAVE(); +} + +/* + * this function returns the scheduler object for the current CPU. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the current CPU identifier. + * 2) retrieve the current scheduler object. + */ + +t_error scheduler_current(o_scheduler** scheduler) +{ + i_cpu cpu; + + /* + * 0) + */ + + if (scheduler == NULL) + CORE_ESCAPE("the 'scheduler' argument is null"); + + /* + * 1) + */ + + if (cpu_current(&cpu) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the current CPU object"); + + /* + * 2) + */ + + if (set_get(_scheduler.schedulers, cpu, (void**)scheduler) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the scheduler from the set"); + + CORE_LEAVE(); +} + +/* + * this function initializes the scheduler manager. + * + * steps: + * + * 1) display a message. + * 2) initialize the manager's structure. + * 3) initialize the quantum. + * 4) retrieve the number of CPUs. + * 5) reserve the set of schedulers. + * 6) go through the CPUs. + * a) retrieve the CPU object. + * b) build the scheduler object. + * g) add the scheduler to the set of schedulers. + * 7) retrieve the currently running scheduler. + * 8) set the scheduler's current thread as being the kernel thread. + * 9) call the machine. + */ + +t_error scheduler_initialize(void) +{ + o_scheduler* scheduler; + t_setsz ncpus; + s_iterator it; + t_state st; + o_cpu* o; + + /* + * 1) + */ + + module_call(console, message, + '+', "initializing the scheduler manager\n"); + + /* + * 2) + */ + + memset(&_scheduler, 0x0, sizeof (m_scheduler)); + + /* + * 3) + */ + + _scheduler.quantum = SCHEDULER_QUANTUM; + + /* + * 4) + */ + + if (set_size(_cpu.cpus, &ncpus) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the number of active CPUs"); + + /* + * 5) + */ + + if (set_reserve(array, + SET_OPTION_ALLOCATE, + ncpus, + sizeof (o_scheduler), + &_scheduler.schedulers) != ERROR_OK) + CORE_ESCAPE("unable to reserve a set for the schedulers"); + + /* + * 6) + */ + + set_foreach(SET_OPTION_FORWARD, _cpu.cpus, &it, st) + { + o_scheduler scheduler; + + /* + * a) + */ + + if (set_object(_cpu.cpus, it, (void**)&o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the CPU object"); + + /* + * b) + */ + + scheduler.cpu = o->id; + scheduler.thread = ID_UNUSED; + scheduler.timeslice = _scheduler.quantum; + scheduler.priority = 0; + scheduler.state = SCHEDULER_STATE_STOP; + + /* + * g) + */ + + if (set_append(_scheduler.schedulers, &scheduler) != ERROR_OK) + CORE_ESCAPE("unable to append the CPU's scheduler to the set"); + } + + /* + * 7) + */ + + if (scheduler_current(&scheduler) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the current CPU's scheduler"); + + /* + * 8) + */ + + scheduler->thread = _kernel.thread; + + /* + * 9) + */ + + if (machine_call(scheduler, initialize) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function just reinitializes the scheduler manager. + * + * steps: + * + * 1) display a message. + * 2) call the machine. + */ + +t_error scheduler_clean(void) +{ + /* + * 1) + */ + + module_call(console, message, + '+', "cleaning the scheduler manager\n"); + + /* + * 2) + */ + + if (machine_call(scheduler, clean) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +#endif diff --git a/kaneton/core/segment/Makefile b/kaneton/core/segment/Makefile new file mode 100644 index 0000000..25877a4 --- /dev/null +++ b/kaneton/core/segment/Makefile @@ -0,0 +1,76 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton.STABLE/kaneton/core/segment/Makefile +# +# created julien quintard [sun jun 10 17:02:46 2007] +# updated julien quintard [sun nov 28 19:44:24 2010] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/core/segment + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +SEGMENT_C := segment-fit.c + +SEGMENT_O := $(SEGMENT_C:.c=.o) + +SEGMENT_INCLUDE := $(_CORE_INCLUDE_DIR_)/segment.h + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(_SEGMENT_LO_) + +$(_SEGMENT_LO_): $(SEGMENT_O) + $(call env_remove,$(_SEGMENT_LO_),) + + $(call env_archive,$(_SEGMENT_LO_),$(SEGMENT_O),) + +clear: + $(call env_remove,$(SEGMENT_O),) + + $(call env_remove,$(_SEGMENT_LO_),) + + $(call env_purge,) + +prototypes: + $(call env_prototypes,$(SEGMENT_INCLUDE),) + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(SEGMENT_C),) + +dependencies: + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/core/segment/segment-fit.c b/kaneton/core/segment/segment-fit.c new file mode 100644 index 0000000..fb5674b --- /dev/null +++ b/kaneton/core/segment/segment-fit.c @@ -0,0 +1,2132 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/segment/segment-fit.c + * + * created julien quintard [fri feb 11 03:04:40 2005] + * updated julien quintard [fri apr 8 09:54:03 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * the segment manager provides functionalities regarding the physical + * memory management. + * + * among these functionalities, the manager can reserve and release + * segments but also modify some of their properties such as a segment's + * permissions, owner, options etc. + * + * the manager also provides functions for manipulating existing segments + * such as resizing, splitting and copying. + * + * every segment is identifier by a 64-bit identified which the caller + * must use to manipulate its segment. + * + * unlike regions, segments are system-wide i.e every segment is unique. + * therefore, a single set of segments is maintained by the segment + * manager while every segment is linked to its owner i.e its address + * space. + * + * however, the address space manager also maintains a set for the segments + * belonging to a specific address space. for more information, please + * refer to the address space manager. + * + * finally, the term segment must not be confound with architecture-specific + * components. for instance, the Intel x86 architecture terminology also + * makes use of segment to describe segmentation elements. + * + * [XXX:improvement] implement a segment_grab() to reserve a specific segment + * or add options to segment_reserve() very much like + * region_reserve(). this function will be useful for + * reserving memory-mapped devices. + * [XXX:improvement] the segment_resize() function can render some regions + * invalid. the system would have no choice but to either + * scan all the regions and update them or refuse the + * segment resizing; or keep a set of the regions mapping + * a segment in order to perform this operation more + * efficiently. + */ + +/* + * ---------- algorithm ------------------------------------------------------- + * + * this implementation relies on the fit algorithm which can be further + * configured through the SEGMENT_FIT macro: + * + * o FIT_FIRST: first fit algorithm - the first large enough space is taken. + * o FIT_BEST: best fit algorithm - the smaller space is taken. + * o etc. + * + * finally, note that this implementation assigns segment identifiers + * in order to maintain order within the set of segments. therefore, there + * is a direct association between a segment's address and its identifier. + * indeed, every segment identifier is computed as follows: + * + * id = address / frame size + * + * this way a segment identifier easily represents the frame---physical + * memory unit---number of the first segment's address. + * + * note however that on most architectures, the smallest unit of physical + * memory is 1 byte, in which case: id = address. + */ + +#if (SEGMENT_ALGORITHM == SEGMENT_ALGORITHM_FIT) + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * include the machine-specific definitions required by the core. + */ + +machine_include(segment); + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the segment manager. + */ + +m_segment _segment; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function shows a segment's attributes. + * + * steps: + * + * 1) retrieve the segment object. + * 2) compute the options string. + * 3) compute the permissions string. + * 4) display the segment's attributes. + * 5) call the machine. + */ + +t_error segment_show(i_segment segid, + mt_margin margin) +{ + char perms[4]; + char options[2]; + o_segment* o; + + /* + * 1) + */ + + if (segment_get(segid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the segment object"); + + /* + * 2) + */ + + if (o->options & SEGMENT_OPTION_SYSTEM) + options[0] = 's'; + else + options[0] = '.'; + + options[1] = '\0'; + + /* + * 3) + */ + + if (o->permissions & PERMISSION_READ) + perms[0] = 'r'; + else + perms[0] = '.'; + + if (o->permissions & PERMISSION_WRITE) + perms[1] = 'w'; + else + perms[1] = '.'; + + if (o->permissions & PERMISSION_EXEC) + perms[2] = 'x'; + else + perms[2] = '.'; + + perms[3] = '\0'; + + /* + * 4) + */ + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + "segment:\n", + MODULE_CONSOLE_MARGIN_VALUE(margin)); + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " core: id(%qd) range(0x%08x - 0x%08x) permissions(%s) " + "size(0x%x) options(%s) as(%qd)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + o->id, + o->address, + o->address + o->size - 1, + perms, + o->size, + options, + o->as); + + /* + * 5) + */ + + if (machine_call(segment, show, segid, margin) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function dumps the segment manager. + * + * steps: + * + * 1) display general information. + * 2) retrieve the size of the set of segments. + * 3) show every segment object. + * 4) call the machine. + */ + +t_error segment_dump(void) +{ + o_segment* data; + t_setsz size; + s_iterator i; + t_state s; + t_uint32 j; + + /* + * 1) + */ + + module_call(console, message, + '#', "segment manager:\n"); + + module_call(console, message, + '#', " core: base(0x%08x) size(0x%08x) segments(%qd)\n", + _segment.base, + _segment.size, + _segment.segments); + + /* + * 2) + */ + + if (set_size(_segment.segments, &size) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the size of the set of segments"); + + /* + * 3) + */ + + module_call(console, message, + '#', " segments: id(%qd) size(%qd)\n", + _segment.segments, + size); + + set_foreach(SET_OPTION_FORWARD, _segment.segments, &i, s) + { + if (set_object(_segment.segments, i, (void**)&data) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the object's identifier from the set"); + + if (segment_show(data->id, + 3 * MODULE_CONSOLE_MARGIN_SHIFT) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the object"); + } + + /* + * 4) + */ + + module_call(console, message, + '#', " vault: size(%u)\n", + SEGMENT_VAULT_SIZE); + + for (j = 0; j < SEGMENT_VAULT_SIZE; j++) + { + if (_segment.vault[j].state == SEGMENT_VAULT_STATE_AVAILABLE) + continue; + + module_call(console, message, + '#', " core: address(0x%08x) size(0x%x)\n", + _segment.vault[j].address, + _segment.vault[j].size); + } + + /* + * 5) + */ + + if (machine_call(segment, dump) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function marks an area of physical memory as being temporarily + * unreservable. + * + * this functionality has been introduced in order to counter the following + * scenario. + * + * let us recall that the function segment_inject() calls set_add(). should + * set_add() need to expand the array, malloc() would be called which, may + * it need extra memory, would call map_reserve() and, in turn, + * segment_reserve() and segment_space(). segment_space() could then return + * the address of the memory area that the caller was trying to inject in + * order to secure it. + * + * the following function can therefore be used to mark a segment in order + * to render it unreservable until enough memory is allocated to the set + * manager to handle an additional element. + * + * steps: + * + * 1) go through the vault entries. + * a) if the entry is available, stop looking for one. + * 2) if no available entry has been found, return a fatal error. + * 3) register the given zone and mark the entry as being used. + * 4) call the machine. + */ + +t_error segment_protect(t_paddr address, + t_psize size) +{ + t_uint32 i; + + /* + * 1) + */ + + for (i = 0; i < SEGMENT_VAULT_SIZE; i++) + { + /* + * a) + */ + + if (_segment.vault[i].state == SEGMENT_VAULT_STATE_AVAILABLE) + break; + } + + /* + * 2) + */ + + if (i == SEGMENT_VAULT_SIZE) + CORE_ESCAPE("unable to protect this zone as the vault is already full"); + + /* + * 3) + */ + + _segment.vault[i].state = SEGMENT_VAULT_STATE_USED; + _segment.vault[i].address = address; + _segment.vault[i].size = size; + + /* + * 4) + */ + + if (machine_call(segment, protect, address, size) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function performs the opposite action of segment_protect() i.e + * it unmarks a memory area. + * + * this function is usally called after the segment has been successfully + * registered in the manager so that it no longer needs to be protected + * against reservation. + * + * steps: + * + * 1) go through the vault entries. + * a) if the zone to unprotect is found, stop. + * 2) if the zone has not been located, return an error. + * 3) mark the entry as available. + * 4) call the machine. + */ + +t_error segment_unprotect(t_paddr address, + t_psize size) +{ + t_uint32 i; + + /* + * 1) + */ + + for (i = 0; i < SEGMENT_VAULT_SIZE; i++) + { + /* + * a) + */ + + if ((_segment.vault[i].state == SEGMENT_VAULT_STATE_USED) && + (_segment.vault[i].address == address) && + (_segment.vault[i].size == size)) + break; + } + + /* + * 2) + */ + + if (i == SEGMENT_VAULT_SIZE) + CORE_ESCAPE("unable to locate the zone to unprotect"); + + /* + * 3) + */ + + memset(&_segment.vault[i], 0x0, sizeof (s_segment_zone)); + + /* + * 4) + */ + + if (machine_call(segment, unprotect, address, size) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function returns true if the given address lies in a protected + * zone, false otherwise. + * + * steps: + * + * 1) go through the vault entries. + * a) if the given address lies in the entry's zone and matches the + * address space, return true. + */ + +t_error segment_protected(t_paddr address) +{ + t_uint32 i; + + /* + * 1) + */ + + for (i = 0; i < SEGMENT_VAULT_SIZE; i++) + { + /* + * a) + */ + + if ((_segment.vault[i].state == SEGMENT_VAULT_STATE_USED) && + (_segment.vault[i].address <= address) && + (address < (_segment.vault[i].address + _segment.vault[i].size))) + CORE_TRUE(); + } + + CORE_FALSE(); +} + +/* + * this function tries to find free space in the segment space via through + * first fit algorithm. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the address space object. + * 2) if there is no segment... + * a) if there is not enough space in the whole segment space to + * satisfy the request, return an error. + * b) if there is, return the base address. + * 3) retrieve the very first segment object. + * 4) if there is enough space between the beginning of the segment space + * and the first segment, use it. + * 5) go through the segments following the first one. + * a) retrieve the segment object. + * b) try to locate the next segment or exit to loop otherwise. + * c) retrieve the next object. + * d) if there is enough space between the current object and the next + * one, use it. + * 6) the function has gone through the segments without finding space. + * retrieve the last segment object. + * 7) if there is enough space between the end of the last segment and + * the end of the segment space, use it. + * 8) if the function reaches this point, the request cannot be satisfied + * and an error is returned. + */ + +t_error segment_fit_first(i_as asid, + t_psize size, + t_paddr* address) +{ + o_segment* current; + t_state state; + o_segment* head; + o_segment* tail; + s_iterator i; + o_as* as; + + /* + * 0) + */ + + if (size == 0) + CORE_ESCAPE("unable to find space for zero bytes"); + + if (address == NULL) + CORE_ESCAPE("the 'address' argument is null"); + + if ((size % ___kaneton$framesz) != 0) + CORE_ESCAPE("the size is not aligned on the machine's frame size: '%u'", + ___kaneton$framesz); + + /* + * 1) + */ + + if (as_get(asid, &as) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the address space object"); + + /* + * 2) + */ + + if (set_head(_segment.segments, &i) == ERROR_FALSE) + { + /* + * a) + */ + + if (_segment.size < size) + CORE_ESCAPE("there is not enough memory to satisfy the segment " + "reservation"); + + /* + * b) + */ + + for (*address = _segment.base; + *address < (_segment.base + _segment.size); + *address = *address + ___kaneton$framesz) + { + /* + * i) + */ + + if (segment_protected(*address) == ERROR_FALSE) + CORE_LEAVE(); + } + + /* + * c) + */ + + CORE_ESCAPE("unable to find available memory"); + } + + /* + * 3) + */ + + if (set_object(_segment.segments, i, (void**)&head) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the very first segment object"); + + /* + * 4) + */ + + for (*address = _segment.base; + (*address + size) <= head->address; + *address = *address + ___kaneton$framesz) + { + if (segment_protected(*address) == ERROR_FALSE) + CORE_LEAVE(); + } + + /* + * 5) + */ + + set_foreach(SET_OPTION_FORWARD, _segment.segments, &i, state) + { + o_segment* next; + s_iterator j; + + /* + * a) + */ + + if (set_object(_segment.segments, + i, + (void**)¤t) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the segment object corresponding " + "to its identifier"); + + /* + * b) + */ + + if (set_next(_segment.segments, i, &j) == ERROR_FALSE) + break; + + /* + * c) + */ + + if (set_object(_segment.segments, j, (void**)&next) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the next segment in the set"); + + /* + * d) + */ + + for (*address = current->address + current->size; + (*address + size) <= next->address; + *address = *address + ___kaneton$framesz) + { + if (segment_protected(*address) == ERROR_FALSE) + CORE_LEAVE(); + } + } + + /* + * 6) + */ + + if (set_tail(_segment.segments, &i) == ERROR_FALSE) + CORE_ESCAPE("unable to locate the last segment in the set"); + + if (set_object(_segment.segments, i, (void**)&tail) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the segment object"); + + /* + * 7) + */ + + for (*address = tail->address + tail->size; + (*address + size) <= (_segment.base + _segment.size); + *address = *address + ___kaneton$framesz) + { + if (segment_protected(*address) == ERROR_FALSE) + CORE_LEAVE(); + } + + /* + * 8) + */ + + CORE_ESCAPE("unable to locate enough space between the existing segments " + "to satisfy the reservation request"); +} + +/* + * this function tries to locate an available slot of the given size. + * + * note that this function is a wrapper above the suppoted algorithms. + * + * steps: + * + * 0) verify the arguments. + * 1) forward the call to the appropriate algorithm + */ + +t_error segment_space(i_as asid, + t_psize size, + t_paddr* address) +{ + /* + * 0) + */ + + if (size == 0) + CORE_ESCAPE("unable to find space for zero bytes"); + + if (address == NULL) + CORE_ESCAPE("the 'address' argument is null"); + + if ((size % ___kaneton$framesz) != 0) + CORE_ESCAPE("the size is not aligned on the machine's frame size: '%u'", + ___kaneton$framesz); + + /* + * 1) + */ + + switch (SEGMENT_FIT) + { + case FIT_FIRST: + { + if (segment_fit_first(asid, size, address) != ERROR_OK) + CORE_ESCAPE("unable to locate space through the first-fit " + "algorithm"); + + break; + } + default: + CORE_ESCAPE("unknown segment algorithm '%u'", + SEGMENT_FIT); + } + + CORE_LEAVE(); +} + +/* + * this function clones a segment i.e reserves an identical segment with + * the same content. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the segment object to clone. + * 2) check if the segment to clone is a system segment, in which case + * an error is returned. + * 3) reserve a new segment of the same size with the write permission + * so that data can be copied to this segment. + * 4) same the segment-to-clone's permissions. + * 5) copy the data depending on the segment-to-clone's permissions. + * A) if the segment-to-clone is not readable... + * a) change its permissions to readable. + * b) copy the data to the new segment. + * c) set the permissions back to what they were. + * B) if it is readable... + * a) copy the data to the new segment. + * 6) set the permissions to the new segment so that they match the ones + * of the original one's. + * 7) call the machine. + */ + +t_error segment_clone(i_as asid, + i_segment old, + i_segment* new) +{ + o_segment* from; + t_permissions perms; + + /* + * 0) + */ + + if (new == NULL) + CORE_ESCAPE("the 'new' argument is null"); + + /* + * 1) + */ + + if (segment_get(old, &from) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the segment object"); + + /* + * 2) + */ + + if (from->options & SEGMENT_OPTION_SYSTEM) + CORE_ESCAPE("unable to clone a system segment"); + + /* + * 3) + */ + + if (segment_reserve(asid, + from->size, + PERMISSION_WRITE, + from->options, + new) != ERROR_OK) + CORE_ESCAPE("unable to reserve a segment"); + + /* + * 4) + */ + + perms = from->permissions; + + /* + * 5) + */ + + if (!(perms & PERMISSION_READ)) + { + /* + * A) + */ + + /* + * a) + */ + + if (segment_permissions(old, PERMISSION_READ) != ERROR_OK) + CORE_ESCAPE("unable to modify the segment's permissions"); + + /* + * b) + */ + + if (segment_copy(*new, 0, old, 0, from->size) != ERROR_OK) + CORE_ESCAPE("unable to copy from the source segment to " + "the cloned one"); + + /* + * c) + */ + + if (segment_permissions(old, perms) != ERROR_OK) + CORE_ESCAPE("unable to modify the segment to its original " + "permissions"); + } + else + { + /* + * B) + */ + + /* + * a) + */ + + if (segment_copy(*new, 0, old, 0, from->size) != ERROR_OK) + CORE_ESCAPE("unable to copy from the source segment to " + "the cloned one"); + } + + /* + * 6) + */ + + if (segment_permissions(*new, perms) != ERROR_OK) + CORE_ESCAPE("unable to set the permissions on the cloned segment"); + + /* + * 7) + */ + + if (machine_call(segment, clone, asid, old, new) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function injects a pre-allocated segment object in an address space. + * + * steps: + * + * 0) retrieve the address space object. + * 1) fill the remaining object's attributes, especially the segment + * identifier. + * 2) set the segment identifier to return to the caller. + * 3) protect the zone so that nobody can reserve it until it is added + * to the sets. + * 4) add the segment object to the set of segments but also the segment + * identifier to the address space's set of segments. + * 5) unprotect the zone. + * 6) call the machine. + */ + +t_error segment_inject(i_as asid, + o_segment* object, + i_segment* id) +{ + o_as* as; + + /* + * 0) + */ + + if (object == NULL) + CORE_ESCAPE("the 'object' argument is null"); + + if (object->size == 0) + CORE_ESCAPE("unable to inject a zero-sized segment object"); + + if ((object->size % ___kaneton$framesz) != 0) + CORE_ESCAPE("the size is not aligned on the machine's frame size: '%u'", + ___kaneton$framesz); + + if (object->permissions & PERMISSION_INVALID) + CORE_ESCAPE("invalid object's permissions"); + + if (id == NULL) + CORE_ESCAPE("the 'id' argument is null"); + + /* + * 1) + */ + + if (as_get(asid, &as) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the address space segment"); + + /* + * 2) + */ + + object->id = SEGMENT_IDENTIFIER(object); + object->as = asid; + + /* + * 3) + */ + + *id = object->id; + + /* + * 4) + */ + + if (segment_protect(object->address, object->size) != ERROR_OK) + CORE_ESCAPE("unable to protect the zone"); + + /* + * 5) + */ + + if (set_add(_segment.segments, object) != ERROR_OK) + CORE_ESCAPE("unable to add the object to the set of segments"); + + if (set_add(as->segments, &object->id) != ERROR_OK) + CORE_ESCAPE("unable to add the segment identifier to the address space"); + + /* + * 6) + */ + + if (segment_unprotect(object->address, object->size) != ERROR_OK) + CORE_ESCAPE("unable to unprotect the zone"); + + /* + * 7) + */ + + if (machine_call(segment, inject, asid, object, id) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function gives the ownership over a segment to another address space, + * hence migrating the segment from one to the other. + * + * steps: + * + * 1) retrieve the segment object. + * 2) retrieve the source and destination address space objects. + * 3) remove the segment identifier from the source address space's set + * of segments. + * 4) set the segment's new address space owner. + * 5) add the segment identifier to the destination address space's set + * of segments. + * 6) call the machine. + */ + +t_error segment_give(i_segment segid, + i_as asid) +{ + o_segment* o; + o_as* dest; + o_as* src; + + /* + * 1) + */ + + if (segment_get(segid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the segment object"); + + /* + * 2) + */ + + if (as_get(o->as, &src) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the address space object"); + + if (as_get(asid, &dest) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the address space object"); + + /* + * 3) + */ + + if (set_remove(src->segments, segid) != ERROR_OK) + CORE_ESCAPE("unable to remove the object from the set of segments"); + + /* + * 4) + */ + + o->as = asid; + + /* + * 5) + */ + + if (set_add(dest->segments, &segid) != ERROR_OK) + CORE_ESCAPE("unable to add the object to the set of segments"); + + /* + * 6) + */ + + if (machine_call(segment, give, segid, asid) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function resizes a segment. + * + * note that if there is not enough space to enlarge the segment where it + * resides, a new segment is allocated elsewhere and the content is + * transferred to the new one. however, this behaviour can be prevented by + * activating the NORELOCATE option. + * + * the caller should be careful as resizing a segment may render the + * regions mapping it, invalid! + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the segment object to resize, taking care to keep an iterator + * on it. + * 2) perform the resizing depending on the future size. + * A) if the future size is smaller than the current one... + * a) update the object's size. + * b) set the future segment identifier as being the same. + * B) if the future size is larger... + * a) try to locate the following segment. + * #A) if there is such a segment... + * i) retrieve the object. + * ii) keep the next segment's address in a temporary variable. + * #B) otherwise, the segment to resize is the last one... + * i) keep the next used address as being the end of the segment space. + * b) continue depending on the size available until the next used address. + * #A) there is enough space for the segment to expand... + * #1) update the object's size. + * #2) set the future identifier as being the same. + * #B) if there is no way for the segment to be resized in-place... + * #1) if the NORELOCATE option has been activated, do not relocate + * and return an error. + * #2) save the segment's permissions. + * #3) reserve another segment with the write permission activated. + * #4) activate the read permission. + * #5) transfer the content from to the new segment. + * #6) set the new segment's permissions to match the ones of the + * original's. + * #7) release the segment that had to be resized, since no longer + * used. + * 3) call the machine. + */ + +t_error segment_resize(i_segment old, + t_psize size, + t_options options, + i_segment* new) +{ + t_permissions perms; + s_iterator it; + o_segment* o; + + /* + * 0) + */ + + if (size == 0) + CORE_ESCAPE("unable to resize the segment to a zero-byte size"); + + if (new == NULL) + CORE_ESCAPE("the 'new' argument is null"); + + if ((size % ___kaneton$framesz) != 0) + CORE_ESCAPE("the size is not aligned on the machine's frame size: '%u'", + ___kaneton$framesz); + + /* + * 1) + */ + + if (set_locate(_segment.segments, old, &it) != ERROR_OK) + CORE_ESCAPE("unable to locate the object in the set of segments"); + + if (set_object(_segment.segments, it, (void**)&o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the segment object"); + + /* + * 2) + */ + + if (size < o->size) + { + /* + * A) + */ + + /* + * a) + */ + + o->size = size; + + /* + * b) + */ + + *new = old; + } + else + { + t_paddr address; + s_iterator next; + + /* + * B) + */ + + /* + * a) + */ + + if (set_next(_segment.segments, it, &next) == ERROR_TRUE) + { + /* + * #A) + */ + + o_segment* onext; + + /* + * i) + */ + + if (set_object(_segment.segments, next, (void**)&onext) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the segment object"); + + /* + * ii) + */ + + address = onext->address; + } + else + { + /* + * #B) + */ + + /* + * i) + */ + + address = _segment.base + _segment.size; + } + + /* + * b) + */ + + if ((o->address + size) < address) + { + /* + * #A) + */ + + /* + * #1) + */ + + o->size = size; + + /* + * #2) + */ + + *new = old; + } + else + { + /* + * #B) + */ + + /* + * #1) + */ + + if (options & SEGMENT_OPTION_NORELOCATE) + CORE_ESCAPE("unable to resize the segment in-place"); + + /* + * #2) + */ + + perms = o->permissions; + + /* + * #3 + */ + + if (segment_reserve(o->as, + size, + PERMISSION_WRITE, + o->options, + new) != ERROR_OK) + CORE_ESCAPE("unable to reserve a segment"); + + /* + * #4) + */ + + if (segment_permissions(old, PERMISSION_READ) != ERROR_OK) + CORE_ESCAPE("unable to modify the segment's permissions"); + + /* + * #5) + */ + + if (segment_copy(*new, 0, old, 0, o->size) != ERROR_OK) + CORE_ESCAPE("unable to copy from one segment to the other"); + + /* + * #6) + */ + + if (segment_permissions(*new, perms) != ERROR_OK) + CORE_ESCAPE("unable to modify the segment's permissions"); + + /* + * #7) + */ + + if (segment_release(old) != ERROR_OK) + CORE_ESCAPE("unable to release the segment"); + } + } + + /* + * 3) + */ + + if (machine_call(segment, resize, old, size, new) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function splits a segment into two new segments. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the segment and address space objects. + * 2) if the splitting size is larger than the segment's size, return + * an error. + * 3) allocate and build the segment object for the 'right' part of the + * splitting. + * 4) update the current segment i.e the future-'left' part's size. + * 5) set the future segment's identifiers: the left takes the identifier + * of the current segment while the right is computed. + * 6) inject the new 'right' object in the address space. + * 7) call the machine. + */ + +t_error segment_split(i_segment segid, + t_psize size, + i_segment* left, + i_segment* right) +{ + o_as* as; + o_segment* o; + o_segment* n; + i_segment useless; + + /* + * 0 + */ + + if (size == 0) + CORE_ESCAPE("unable to split a segment to an empty size"); + + if (left == NULL) + CORE_ESCAPE("the 'left' argument is null"); + + if (right == NULL) + CORE_ESCAPE("the 'right' argument is null"); + + if ((size % ___kaneton$framesz) != 0) + CORE_ESCAPE("the size is not aligned on the machine's frame size: '%u'", + ___kaneton$framesz); + + /* + * 1) + */ + + if (segment_get(segid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the segment object"); + + if (as_get(o->as, &as) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the address space object"); + + /* + * 2) + */ + + if (size > o->size) + CORE_ESCAPE("the splitted size must be smaller than the current size"); + + /* + * 3) + */ + + if ((n = malloc(sizeof (o_segment))) == NULL) + CORE_ESCAPE("unable to allocate memory for the segment object"); + + n->as = o->as; + n->permissions = o->permissions; + n->options = o->options; + + n->address = o->address + size; + n->size = o->size - size; + + n->id = SEGMENT_IDENTIFIER(n); + + /* + * 4) + */ + + o->size = size; + + /* + * 5) + */ + + *left = segid; + *right = n->id; + + /* + * 6) + */ + + if (segment_inject(o->as, n, &useless) != ERROR_OK) + CORE_ESCAPE("unable to inject the segment object"); + + /* + * 7) + */ + + if (machine_call(segment, split, + segid, size, left, right) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function coalesces two adjacent segments into a single one. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve both the left and right segment objects. + * 2) verify that the segments are adjacent, with the same options and + * permissions etc. + * 3) compute the new segment size. + * 4) return the future identifier i.e the identifier of the left + * segment while the right one will be destroyed. + * 5) release the right segment. + * 6) call the machine. + */ + +t_error segment_coalesce(i_segment left, + i_segment right, + i_segment* id) +{ + o_segment* seg1; + o_segment* seg2; + + /* + * 0) + */ + + if (left == right) + CORE_ESCAPE("unable to coalesce the same segment"); + + if (id == NULL) + CORE_ESCAPE("the 'id' argument is null"); + + /* + * 1) + */ + + if (segment_get(left, &seg1) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the segment object"); + + if (segment_get(right, &seg2) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the segment object"); + + /* + * 2) + */ + + if ((seg1->address + seg1->size) != seg2->address) + CORE_ESCAPE("unable to coalesce non-adjacent segments"); + + if (seg1->as != seg2->as) + CORE_ESCAPE("unable to coalesce segments from different address spaces"); + + if (seg1->permissions != seg2->permissions) + CORE_ESCAPE("unable to coalesce segments with different permissions"); + + if (seg1->options != seg2->options) + CORE_ESCAPE("unable to coalesce segments with different options"); + + /* + * 3) + */ + + seg1->size += seg2->size; + + /* + * 4) + */ + + *id = seg1->id; + + /* + * 5) + */ + + if (segment_release(right) != ERROR_OK) + CORE_ESCAPE("unable to release the segment"); + + /* + * 6) + */ + + if (machine_call(segment, coalesce, + left, right, id) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function reads data from a segment. + * + * note that this function relies on the machine for performing the + * operation since the kaneton kernel is assumed to evolve in a virtual + * environment. the machine probably benefits from advanced functionalities + * for performing such an operation. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the segment object. + * 2) check the segment's permissions along with the operation's boundaries. + * 3) call the machine. + */ + +t_error segment_read(i_segment segid, + t_paddr offs, + void* buffer, + t_psize sz) +{ + o_segment* o; + + /* + * 0) + */ + + if (sz == 0) + CORE_ESCAPE("unable to perform a zero-sized reading"); + + if (buffer == NULL) + CORE_ESCAPE("the 'buffer' argument is null"); + + /* + * 1) + */ + + if (segment_get(segid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the segment object"); + + /* + * 2) + */ + + if (!(o->permissions & PERMISSION_READ)) + CORE_ESCAPE("unable to read data from a non-readable segment"); + + if ((offs + sz) > o->size) + CORE_ESCAPE("out-of-bound operation"); + + /* + * 3) + */ + + if (machine_call(segment, read, segid, offs, buffer, sz) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function write data to a given segment. + * + * note that this function relies on the machine for performing the + * operation since the kaneton kernel is assumed to evolve in a virtual + * environment. the machine probably benefits from advanced functionalities + * for performing such an operation. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the segment object. + * 2) check the segment's permissions along with the operation's boundaries. + * 3) call the machine. + */ + +t_error segment_write(i_segment segid, + t_paddr offs, + const void* buffer, + t_psize sz) +{ + o_segment* o; + + /* + * 0) + */ + + if (sz == 0) + CORE_ESCAPE("unable to perform a zero-sized writing"); + + if (buffer == NULL) + CORE_ESCAPE("the 'buffer' argument is null"); + + /* + * 1) + */ + + if (segment_get(segid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the segment object"); + + /* + * 2) + */ + + if (!(o->permissions & PERMISSION_WRITE)) + CORE_ESCAPE("unable to write a non-writable segment"); + + if (offs + sz > o->size) + CORE_ESCAPE("out-of-bound operation"); + + /* + * 3) + */ + + if (machine_call(segment, write, + segid, offs, buffer, sz) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function copies data from one segment to another. + * + * note that this function relies on the machine for performing the + * operation since the kaneton kernel is assumed to evolve in a virtual + * environment. the machine probably benefits from advanced functionalities + * for performing such an operation. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the segment objects. + * 2) check the segments' permissions along with the operation's boundaries. + * 3) call the machine. + */ + +t_error segment_copy(i_segment dst, + t_paddr offsd, + i_segment src, + t_paddr offss, + t_psize sz) +{ + o_segment* o1; + o_segment* o2; + + /* + * 0) + */ + + if (sz == 0) + CORE_ESCAPE("unable to perform a zero-sized copy"); + + /* + * 1) + */ + + if (segment_get(dst, &o1) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the segment object"); + + if (segment_get(src, &o2) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the segment object"); + + /* + * 2) + */ + + if (!(o1->permissions & PERMISSION_WRITE)) + CORE_ESCAPE("unable to copy from a non-readable segment"); + + if (!(o2->permissions & PERMISSION_READ)) + CORE_ESCAPE("unable to copy to a non-writable segment"); + + if (offsd + sz > o1->size) + CORE_ESCAPE("the source address is out-of-bound"); + + if (offss + sz > o2->size) + CORE_ESCAPE("the destination address is out-of-bound"); + + /* + * 3) + */ + + if (machine_call(segment, copy, + dst, offsd, src, offss, sz) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function reserves a segment given the desired size and permissions. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the address space object. + * 2) allocate memory for the segment object. + * 3) try to locate available space for the segment according to the + * requested size. + * 4) protect the located space from being reserved. + * 5) fill the object's attributes. + * 6) set the identifier to return. + * 7) add the object to the set of segments and the identifier to the + * address space's set of segments. + * 8) unprotect the zone. + * 9) call the machine. + */ + +t_error segment_reserve(i_as asid, + t_psize size, + t_permissions perms, + t_options options, + i_segment* id) +{ + o_as* as; + o_segment* o; + + /* + * 0) + */ + + if (size == 0) + CORE_ESCAPE("unable to reserve a segment with an empty size"); + + if (perms & PERMISSION_INVALID) + CORE_ESCAPE("invalid permissions"); + + if (id == NULL) + CORE_ESCAPE("the 'id' argument is null"); + + if ((size % ___kaneton$framesz) != 0) + CORE_ESCAPE("the size is not aligned on the machine's frame size: '%u'", + ___kaneton$framesz); + + /* + * 1) + */ + + if (as_get(asid, &as) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the address space object"); + + /* + * 2) + */ + + if ((o = malloc(sizeof (o_segment))) == NULL) + CORE_ESCAPE("unable to allocate memory for the segment object"); + + + /* + * 3) + */ + + if (segment_space(asid, size, &o->address) != ERROR_OK) + CORE_ESCAPE("unable to locate unused space for the segment resevation"); + + /* + * 4) + */ + + if (segment_protect(o->address, size) != ERROR_OK) + CORE_ESCAPE("unable to protect the zone"); + + /* + * 5) + */ + + o->as = as->id; + o->size = size; + o->permissions = perms; + o->options = options; + o->id = SEGMENT_IDENTIFIER(o); + + /* + * 6) + */ + + *id = o->id; + + /* + * 7) + */ + + if (set_add(_segment.segments, o) != ERROR_OK) + CORE_ESCAPE("unable to add the object to the set of segments"); + + if (set_add(as->segments, &o->id) != ERROR_OK) + CORE_ESCAPE("unable to add the segment identifier to the addresss space"); + + /* + * 8) + */ + + if (segment_unprotect(o->address, size) != ERROR_OK) + CORE_ESCAPE("unable to unprotect the zone"); + + /* + * 9) + */ + + if (machine_call(segment, reserve, + asid, size, perms, id) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function releases a segment. + * + * steps: + * + * 1) call the machine. + * 2) retrieve the segment and address space objects. + * 4) remove the object from the set of segments and the identifier from + * the address space's set of segments. + */ + +t_error segment_release(i_segment segid) +{ + o_as* as; + o_segment* o; + + /* + * 1) + */ + + if (machine_call(segment, release, segid) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 2) + */ + + if (segment_get(segid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the segment object"); + + if (as_get(o->as, &as) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the address space object"); + + /* + * 3) + */ + + if (set_remove(as->segments, segid) != ERROR_OK) + CORE_ESCAPE("unable to remove the segment identifier from the " + "address space"); + + if (set_remove(_segment.segments, segid) != ERROR_OK) + CORE_ESCAPE("unable to remove the object from the set of segments"); + + CORE_LEAVE(); +} + +/* + * this function modifies the permissions of a given segment. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the segment object. + * 3) assign the new permissions. + * 4) call the machine. + */ + +t_error segment_permissions(i_segment segid, + t_permissions perms) +{ + o_segment* o; + + /* + * 0) + */ + + if (perms & PERMISSION_INVALID) + CORE_ESCAPE("invalid permissions"); + + /* + * 1) + */ + + if (segment_get(segid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the segment object"); + + /* + * 2) + */ + + o->permissions = perms; + + /* + * 3) + */ + + if (machine_call(segment, permissions, segid, perms) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function removes every segment that belongs to the given address space. + * + * steps: + * + * 1) call the machine. + * 2) retrieve the address space object. + * 3) go through the segments of the address space. + * a) retrieve the segment identifier. + * b) release the segment. + */ + +t_error segment_flush(i_as asid) +{ + i_segment* data; + o_as* as; + s_iterator i; + + /* + * 1) + */ + + if (machine_call(segment, flush, asid) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 2) + */ + + if (as_get(asid, &as) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the address space object"); + + /* + * 3) + */ + + while (set_head(as->segments, &i) == ERROR_TRUE) + { + /* + * a) + */ + + if (set_object(as->segments, i, (void**)&data) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the object corresponding to " + "its identifier"); + + /* + * b) + */ + + if (segment_release(*data) != ERROR_OK) + CORE_ESCAPE("unable to release the segment"); + } + + CORE_LEAVE(); +} + +/* + * this functions takes a physical address and returns the segment which + * holds this address. + * + * note that this function returns either true or false depending on the + * success of the look up. + * + * steps: + * + * 0) verify the arguments. + * 1) go through the segments of the set. + * a) retrieve the object. + * b) if the address lies in this segment, return its identifier. + * 2) if no segment has been found, return false; + */ + +t_error segment_locate(t_paddr address, + i_segment* id) +{ + o_segment* object; + t_state state; + s_iterator i; + + /* + * 0) + */ + + assert(id != NULL); + + /* + * 1) + */ + + set_foreach(SET_OPTION_FORWARD, _segment.segments, &i, state) + { + /* + * a) + */ + + assert(set_object(_segment.segments, i, (void**)&object) == ERROR_OK); + + /* + * b) + */ + + if ((object->address <= address) && + (address < (object->address + object->size))) + { + *id = object->id; + + CORE_TRUE(); + } + } + + /* + * 2) + */ + + CORE_FALSE(); +} + +/* + * this function returns true if the segment exists. + */ + +t_error segment_exist(i_segment segid) +{ + if (set_exist(_segment.segments, segid) != ERROR_TRUE) + CORE_FALSE(); + + CORE_TRUE(); +} + +/* + * this function returns a segment object according to its identifier. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the object from the manager's set of segments. + */ + +t_error segment_get(i_segment segid, + o_segment** object) +{ + /* + * 0) + */ + + if (object == NULL) + CORE_ESCAPE("the 'object' argument is null"); + + /* + * 1) + */ + + if (set_get(_segment.segments, segid, (void**)object) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the object from the set of segments"); + + CORE_LEAVE(); +} + +/* + * this function initializes the segment manager according to the base + * and size argument received representing the space of physical memory. + * + * steps: + * + * 1) display a message. + * 2) initialize the segment manager's structure. + * 3) record the base and size in the manager's structure. + * 4) reserve the set of segments which will contain the system's segments. + * 5) call the machine. + */ + +t_error segment_initialize(t_paddr base, + t_psize size) +{ + /* + * 0) + */ + + if (size == 0) + CORE_ESCAPE("unable to initialize the segment manager with a " + "size of zero"); + + if ((base % ___kaneton$framesz) != 0) + CORE_ESCAPE("the base is not aligned on the machine's frame size: '%u'", + ___kaneton$framesz); + + if ((size % ___kaneton$framesz) != 0) + CORE_ESCAPE("the size is not aligned on the machine's frame size: '%u'", + ___kaneton$framesz); + + /* + * 1) + */ + + module_call(console, message, + '+', "initializing the segment manager\n"); + + /* + * 2) + */ + + memset(&_segment, 0x0, sizeof (m_segment)); + + /* + * 3) + */ + + _segment.base = base; + _segment.size = size; + + /* + * 4) + */ + + if (set_reserve(bpt, + SET_OPTION_SORT | SET_OPTION_FREE, + sizeof (o_segment), + SEGMENT_BPT_NODESZ, + &_segment.segments) != ERROR_OK) + CORE_ESCAPE("unable to reserve the segments set"); + + /* + * 5) + */ + + if (machine_call(segment, initialize) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function cleans the segment manager. + * + * steps: + * + * 1) display a message. + * 2) call the machine. + * 3) release all the segments. + * a) retrieve the segment object. + * b) release the segment. + * 4) release the set of segments. + */ + +t_error segment_clean(void) +{ + o_segment* data; + s_iterator i; + + /* + * 1) + */ + + module_call(console, message, + '+', "cleaning the segment manager\n"); + + /* + * 2) + */ + + if (machine_call(segment, clean) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 3) + */ + + while (set_head(_segment.segments, &i) == ERROR_TRUE) + { + /* + * a) + */ + + if (set_object(_segment.segments, i, (void**)&data) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the object corresponding to " + "its identifier"); + + /* + * b) + */ + + if (segment_release(data->id) != ERROR_OK) + CORE_ESCAPE("unable to release the segment"); + } + + /* + * 4) + */ + + if (set_release(_segment.segments) != ERROR_OK) + CORE_ESCAPE("unable to release the segments set"); + + CORE_LEAVE(); +} + +#endif diff --git a/kaneton/core/set/Makefile b/kaneton/core/set/Makefile new file mode 100644 index 0000000..06d6e68 --- /dev/null +++ b/kaneton/core/set/Makefile @@ -0,0 +1,81 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton.STABLE/kaneton/core/set/Makefile +# +# created julien quintard [sun jun 10 17:04:39 2007] +# updated julien quintard [fri dec 3 23:07:14 2010] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/core/set + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +SET_C := set.c \ + set-array.c \ + set-bpt.c \ + set-ll.c \ + set-pipe.c \ + set-stack.c + +SET_O := $(SET_C:.c=.o) + +SET_INCLUDE := $(_CORE_INCLUDE_DIR_)/set.h + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(_SET_LO_) + +$(_SET_LO_): $(SET_O) + $(call env_remove,$(_SET_LO_),) + + $(call env_archive,$(_SET_LO_),$(SET_O),) + +clear: + $(call env_remove,$(SET_O),) + + $(call env_remove,$(_SET_LO_),) + + $(call env_purge,) + +prototypes: + $(call env_prototypes,$(SET_INCLUDE),) + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(SET_C),) + +dependencies: + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/core/set/set-array.c b/kaneton/core/set/set-array.c new file mode 100644 index 0000000..56eab58 --- /dev/null +++ b/kaneton/core/set/set-array.c @@ -0,0 +1,1703 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/set/set-array.c + * + * created julien quintard [fri feb 11 03:04:40 2005] + * updated julien quintard [fri apr 8 09:54:31 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this set implementation provides array data structures. + * + * there are two way to use the arrays: with pointers or with objects. the + * first method is the default one (without SET_OPTION_ALLOCATE); it only + * stores pointers refering to objects. The second method (used with + * SET_OPTION_ALLOCATE) makes copies of objects in the set, using memcpy(). + * + * if the FREE option is activated, objects are freed when they are + * removed from the array or when the array is flushed/released. + * + * the option ORGANISE is used to keep the array as small as possible + * by regularly shifting the elements in order to avoid "holes" in the + * entries. + * + * options: + * SET_OPTION_CONTAINER + * SET_OPTION_SORT + * SET_OPTION_ORGANISE + * SET_OPTION_ALLOCATE + * SET_OPTION_FREE + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the manager's structure. + */ + +extern m_set _set; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function tries to find an object with its identifier and returns + * true or false. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the descriptor. + * 2) search sequentially for the element. + */ + +t_error set_exist_array(i_set setid, + t_id id) +{ + o_set* o; + i_set i; + + /* + * 0) + */ + + assert(id != ID_UNUSED); + + /* + * 1) + */ + + assert(set_descriptor(setid, &o) == ERROR_OK); + + /* + * 2) + */ + + for (i = 0; i < o->u.array.arraysz; ++i) + { + if (o->u.array.array[i] && + *((t_id*)(o->u.array.array[i])) == id) + CORE_TRUE(); + } + + CORE_FALSE(); +} + +/* + * this function shows a set's attributes. + * + * steps: + * + * 1) retrieve the set descriptor. + * 2) build the options string. + * 3) display information on the set. + * 4) go through the set's objects. + * a) retrieve the object's identifier. + * b) warn if an unused identifier has been found though this entry + * is not marked as available. + * c) display the identifier and move on to the next object. + */ + +t_error set_show_array(i_set setid, + mt_margin margin) +{ + o_set* o; + t_setsz i; + t_setsz pos; + t_id id; + char options[6]; + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + if (o->options & SET_OPTION_CONTAINER) + options[0] = 'c'; + else + options[0] = '.'; + + if (o->options & SET_OPTION_SORT) + options[1] = 's'; + else + options[1] = '.'; + + if (o->options & SET_OPTION_ORGANISE) + options[2] = 'o'; + else + options[2] = '.'; + + if (o->options & SET_OPTION_ALLOCATE) + options[3] = 'a'; + else + options[3] = '.'; + + if (o->options & SET_OPTION_FREE) + options[4] = 'f'; + else + options[4] = '.'; + + options[5] = '\0'; + + /* + * 3) + */ + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + "set: id(%qd) type(array) size(%qd) datasz(%u) options(%s) " + "initsz(%u) array(0x%x) arraysz(%qd)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + o->id, + o->size, + o->datasz, + options, + o->u.array.initsz, + o->u.array.array, + o->u.array.arraysz); + + /* + * 4) + */ + + for (i = 0, pos = 0; i < o->u.array.arraysz; ++i) + { + /* + * a) + */ + + if (o->u.array.array[i]) + id = *((t_id*)(o->u.array.array[i])); + else + continue; + + /* + * b) + */ + + if (id == ID_UNUSED) + module_call(console, message, + '!', "warning: unused object detected !\n"); + + /* + * c) + */ + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " object: id(%qd) slot(%qd)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + id, + pos); + + ++pos; + } + + CORE_LEAVE(); +} + +/* + * this function returns an iterator on the first entry of the array. + * + * if there is no element in the array, the function returns false. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) if the array is empty, return false. + * 3) go through the elements, looking for the first used entry. + * 4) set the iterator. + */ + +t_error set_head_array(i_set setid, + s_iterator* iterator) +{ + o_set* o; + t_setsz i; + + /* + * 0) + */ + + assert(iterator != NULL); + + /* + * 1) + */ + + assert(set_descriptor(setid, &o) == ERROR_OK); + + /* + * 2) + */ + + if (o->size == 0) + CORE_FALSE(); + + /* + * 3) + */ + + for (i = 0; i < o->u.array.arraysz; ++i) + { + if (o->u.array.array[i]) + break; + } + + /* + * 4) + */ + + iterator->u.array.i = i; + + CORE_TRUE(); +} + +/* + * this function returns an iterator on the last entry of the array. + * + * if there is no node in the list, the function returns false. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) if the array is empty, return false. + * 3) go through the elements, looking for the last used entry. + * 4) set the iterator. + */ + +t_error set_tail_array(i_set setid, + s_iterator* iterator) +{ + o_set* o; + t_setsz i; + + /* + * 0) + */ + + assert(iterator != NULL); + + /* + * 1) + */ + + assert(set_descriptor(setid, &o) == ERROR_OK); + + /* + * 2) + */ + + if (o->size == 0) + CORE_FALSE(); + + /* + * 3) + */ + + for (i = o->u.array.arraysz - 1; i >= 0; --i) + { + if (o->u.array.array[i]) + break; + } + + /* + * 4) + */ + + iterator->u.array.i = i; + + CORE_TRUE(); +} + +/* + * this function returns an iterator on the entry previous to the given + * iterator. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) go through the elements, looking for the previous used entry. + * 3) return false if no entry was found. + * 4) set the iterator. + */ + +t_error set_previous_array(i_set setid, + s_iterator current, + s_iterator* previous) +{ + o_set* o; + t_setsz i; + + /* + * 0) + */ + + assert(previous != NULL); + + /* + * 1) + */ + + assert(set_descriptor(setid, &o) == ERROR_OK); + + /* + * 2) + */ + + for (i = current.u.array.i - 1; i >= 0; --i) + { + if (o->u.array.array[i]) + break; + } + + /* + * 3) + */ + + if (i == -1) + CORE_FALSE(); + + /* + * 4) + */ + + previous->u.array.i = i; + + CORE_TRUE(); +} + +/* + * this function returns an iterator on the element following the one + * pointed by the given iterator. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) go through the elements, looking for the next used entry. + * 3) return false if no entry was found. + * 4) set the iterator. + */ + +t_error set_next_array(i_set setid, + s_iterator current, + s_iterator* next) +{ + o_set* o; + t_setsz i; + + /* + * 0) + */ + + assert(next != NULL); + + /* + * 1) + */ + + assert(set_descriptor(setid, &o) == ERROR_OK); + + /* + * 2) + */ + + for (i = current.u.array.i + 1; i < o->u.array.arraysz; ++i) + { + if (o->u.array.array[i]) + break; + } + + /* + * 3) + */ + + if (i >= o->u.array.arraysz) + CORE_FALSE(); + + /* + * 4) + */ + + next->u.array.i = i; + + CORE_TRUE(); +} + +/* + * this function is used when no room is found in the array. it expands + * the array and copies data, if required. + * + * note that the given data is inserted in the first new slot. + * + * steps: + * + * 0) verify the arguments. + * 1) initialize the index with the set's current size. + * 2) computes the new size according to the options. + * 3) expands the array and set the new size. + * 4) insert the data in the new slot i.e at the saved index i.e the end + * of previous array. + * 5) initialize the new entries as available i.e unused. + */ + +t_error set_expand_array(o_set *object, + void *data) +{ + t_setsz i; + t_setsz sz; + + /* + * 0) + */ + + if (object == NULL) + CORE_ESCAPE("the 'object' argument is null"); + + /* + * 1) + */ + + i = object->u.array.arraysz; + + /* + * 2) + */ + + if (object->options & SET_OPTION_ORGANISE) + sz = object->u.array.arraysz + 1; + else + sz = object->u.array.arraysz * 2; + + /* + * 3) + */ + + if ((object->u.array.array = realloc(object->u.array.array, + sz * sizeof (void*))) == NULL) + CORE_ESCAPE("unable to reallocate memory for the array"); + + object->u.array.arraysz = sz; + + /* + * 4) + */ + + object->u.array.array[i] = data; + + /* + * 5) + */ + + for (++i; i < object->u.array.arraysz; ++i) + object->u.array.array[i] = NULL; + + CORE_LEAVE(); +} + +/* + * this function inserts an element at the specified position. this + * function is used by many others but is not part of the interface. + * + * steps: + * + * 0) verify the arguments. + * 1) if the position is at the end, expand the array and insert the + * data. + * 2) if the entry is available, insert the data and exit. + * 3) otherwise, start at the position and walk through the entries trying + * to find a free slot. + * 4) if none has been found, expand the array in order to create free slots. + * 5) one found, walk back and shift the elements to the right so that + * the entry at the requested position becomes available. + * 6) insert the data at the desired position. + */ + +t_error set_insert_array_at(o_set *object, + t_setsz pos, + void *data) +{ + t_setsz limit; + + /* + * 0) + */ + + if (object == NULL) + CORE_ESCAPE("the 'object' argument is null"); + + /* + * 1) + */ + + if (object->u.array.arraysz == pos) + { + if (set_expand_array(object, data) != ERROR_OK) + CORE_ESCAPE("unable to expand the array"); + + CORE_LEAVE(); + } + + /* + * 2) + */ + + if (object->u.array.array[pos] == NULL) + { + object->u.array.array[pos] = data; + + CORE_LEAVE(); + } + + /* + * 3) + */ + + for (limit = pos; limit < object->u.array.arraysz; ++limit) + { + if (object->u.array.array[limit] == NULL) + break; + } + + /* + * 4) + */ + + if (limit == object->u.array.arraysz) + { + if (set_expand_array(object, + object->u.array.array[limit - 1]) != ERROR_OK) + CORE_ESCAPE("unable to expand the array"); + } + + /* + * 5) + */ + + for (; limit > pos; --limit) + { + object->u.array.array[limit] = object->u.array.array[limit - 1]; + } + + /* + * 6) + */ + + object->u.array.array[pos] = data; + + CORE_LEAVE(); +} + +/* + * this function inserts a new entry at the head of the array. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) check if this operation can be performed in this set. + * 3) if the ALLOCATE option has been activated, clone the object. + * 4) insert the element at the head. + * 5) update the size of the set. + */ + +t_error set_insert_array(i_set setid, + void* data) +{ + o_set* o; + void* cpy; + + /* + * 0) + */ + + if (data == NULL) + CORE_ESCAPE("the 'data' argument is null"); + + if (*((t_id*)data) == ID_UNUSED) + CORE_ESCAPE("the object to insert must begin with a valid identifier"); + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + if (o->options & SET_OPTION_SORT) + CORE_ESCAPE("this operation is not allowed for sorted sets"); + + /* + * 3) + */ + + if (o->options & SET_OPTION_ALLOCATE) + { + if ((cpy = malloc(o->datasz)) == NULL) + CORE_ESCAPE("unable to allocate memory for the object's copy"); + + memcpy(cpy, data, o->datasz); + + data = cpy; + } + + /* + * 4) + */ + + if (set_insert_array_at(o, 0, data) != ERROR_OK) + { + if (o->options & SET_OPTION_ALLOCATE) + free(data); + + CORE_ESCAPE("unable to insert the object in the set"); + } + + /* + * 5) + */ + + ++o->size; + + CORE_LEAVE(); +} + +/* + * this function inserts a new entry at the tail of the array. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) check if this operation can be performed in this set. + * 3) if the ALLOCATE option has been activated, clone the object. + * 4) locate the last available slot. + * 5) insert the data at this specific location. + * 6) update the size of the set. + */ + +t_error set_append_array(i_set setid, + void* data) +{ + o_set* o; + t_setsz i; + void* cpy; + + /* + * 0) + */ + + if (data == NULL) + CORE_ESCAPE("the 'data' argument is null"); + + if (*((t_id*)data) == ID_UNUSED) + CORE_ESCAPE("the object to append must begin with a valid identifier"); + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + if (o->options & SET_OPTION_SORT) + CORE_ESCAPE("unable to append an object to a sorted set"); + + /* + * 3) + */ + + if (o->options & SET_OPTION_ALLOCATE) + { + if (!(cpy = malloc(o->datasz))) + CORE_ESCAPE("unable to allocate memory for the object's copy"); + + memcpy(cpy, data, o->datasz); + + data = cpy; + } + + /* + * 4) + */ + + if (o->size > 0) + { + for (i = o->u.array.arraysz; i > 0; --i) + { + if (o->u.array.array[i - 1]) + break; + } + } + else + i = 0; + + /* + * 5) + */ + + if (set_insert_array_at(o, i, data) != ERROR_OK) + { + if (o->options & SET_OPTION_ALLOCATE) + free(data); + + CORE_ESCAPE("unable to insert the object in the set"); + } + + /* + * 6) + */ + + ++o->size; + + CORE_LEAVE(); +} + +/* + * this function inserts an object before the one referenced by the iterator. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) check if this operation can be performed in this set. + * 3) if the ALLOCATE option has been activated, clone the object. + * 4) insert the object at the location pointed to by the iterator, hence + * shifting the following entries to the right. + * 5) update the size of the set. + */ + +t_error set_before_array(i_set setid, + s_iterator iterator, + void* data) +{ + o_set* o; + t_setsz i; + void* cpy; + + /* + * 0) + */ + + if (data == NULL) + CORE_ESCAPE("the 'data' argument is null"); + + if (*((t_id*)data) == ID_UNUSED) + CORE_ESCAPE("the object to insert must begin with a valid identifier"); + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + if (o->options & SET_OPTION_SORT) + CORE_ESCAPE("unable to insert at a specific location in a sorted set"); + + /* + * 3) + */ + + if (o->options & SET_OPTION_ALLOCATE) + { + if (!(cpy = malloc(o->datasz))) + CORE_ESCAPE("unable to allocate memory for the object's copy"); + + memcpy(cpy, data, o->datasz); + + data = cpy; + } + + /* + * 4) + */ + + i = iterator.u.array.i; + + if (i < 0) + i = 0; + + if (set_insert_array_at(o, i, data) != ERROR_OK) + { + if (o->options & SET_OPTION_ALLOCATE) + free(data); + + CORE_ESCAPE("unable to insert the object in the set"); + } + + /* + * 5) + */ + + ++o->size; + + CORE_LEAVE(); +} + +/* + * this function inserts a new object after the one referenced by the iterator. + * + * steps: + * + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) check if this operation can be performed in this set. + * 3) if the ALLOCATE option has been activated, clone the object. + * 4) insert the object at the location following the one pointed + * to by the iterator. + * 5) update the size of the set. + */ + +t_error set_after_array(i_set setid, + s_iterator iterator, + void* data) +{ + o_set* o; + t_setsz i; + void* cpy; + + /* + * 0) + */ + + if (data == NULL) + CORE_ESCAPE("the 'data' argument is null"); + + if (*((t_id*)data) == ID_UNUSED) + CORE_ESCAPE("the object to insert must begin with a valid identifier"); + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + if (o->options & SET_OPTION_SORT) + CORE_ESCAPE("unable to insert at a precise position in a sorted set"); + + /* + * 3) + */ + + if (o->options & SET_OPTION_ALLOCATE) + { + if (!(cpy = malloc(o->datasz))) + CORE_ESCAPE("unable to allocate memory for the object's copy"); + + memcpy(cpy, data, o->datasz); + + data = cpy; + } + + /* + * 4) + */ + + i = iterator.u.array.i + 1; + + if (i > o->u.array.arraysz) + i = o->u.array.arraysz; + + if (set_insert_array_at(o, i, data) != ERROR_OK) + { + if (o->options & SET_OPTION_ALLOCATE) + free(data); + + CORE_ESCAPE("unable to insert the object in the set"); + } + + /* + * 5) + */ + + ++o->size; + + CORE_LEAVE(); +} + +/* + * this function adds an object to the array. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) clone the object if required. + * 3) inserts the element depending on the options. + * A) if the SORT option has been activated... + * i) find the place of the new element. + * ii) copy in the right place. + * iii) expand the array if necessary. + * B) otherwise... + * i) try to find a free place. + * ii) expand the array if necessary. + * 4) update the set's size. + */ + +t_error set_add_array(i_set setid, + void* data) +{ + o_set* o; + t_setsz i; + t_id id; + t_id current; + void* cpy; + t_uint8 empty; + t_setsz last; + + /* + * 0) + */ + + if (data == NULL) + CORE_ESCAPE("the 'data' argument is null"); + + if (*((t_id*)data) == ID_UNUSED) + CORE_ESCAPE("the object to add must begin with a valid identifier"); + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + if (o->options & SET_OPTION_ALLOCATE) + { + if (!(cpy = malloc(o->datasz))) + CORE_ESCAPE("unable to allocate memory for the object's copy"); + + memcpy(cpy, data, o->datasz); + + data = cpy; + } + + /* + * 3) + */ + + if (o->options & SET_OPTION_SORT) + { + /* + * A) + */ + + /* + * i) + */ + + empty = 1; + last = -1; + + id = *((t_id*)data); + + for (i = 0; i < o->u.array.arraysz; ++i) + { + if (o->u.array.array[i] != NULL) + { + current = *((t_id*)o->u.array.array[i]); + + empty = 0; + + if (current == id) + { + if (o->options & SET_OPTION_ALLOCATE) + free(data); + + CORE_ESCAPE("identifier collision detected in the set " + "%qd on the object identifier %qd", + o->id, id); + } + + if (current > id) + break; + + last = i; + } + } + + /* + * ii) + */ + + ++last; + + if (empty) + last = i = o->u.array.arraysz / 2; + + if (last < o->u.array.arraysz) + { + if (set_insert_array_at(o, last, data) != ERROR_OK) + { + if (o->options & SET_OPTION_ALLOCATE) + free(data); + + CORE_ESCAPE("unable to insert the object in the set"); + } + } + + /* + * iii) + */ + + if (last >= o->u.array.arraysz) + { + if (set_expand_array(o, data) != ERROR_OK) + { + if (o->options & SET_OPTION_ALLOCATE) + free(data); + + CORE_ESCAPE("unable to expand the array"); + } + } + } + else + { + /* + * B) + */ + + /* + * i) + */ + + for (i = 0; i < o->u.array.arraysz; ++i) + { + if (o->u.array.array[i] == NULL) + { + o->u.array.array[i] = data; + + break; + } + } + + /* + * ii) + */ + + if (i == o->u.array.arraysz) + { + if (set_expand_array(o, data) != ERROR_OK) + { + if (o->options & SET_OPTION_ALLOCATE) + free(data); + + CORE_ESCAPE("unable to expand the array"); + } + } + } + + /* + * 4) + */ + + ++o->size; + + CORE_LEAVE(); +} + +/* + * this function removes an object from the array, setting the identifier + * field of the entry as 'unused'. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) if there is no object, return an error. + * 3) try to locate the object according to its identifier. + * 4) if not found, return an error. + * 5) if the ORGANIZE option has been activated, shift the elements to + * the left and re-allocate the array with the necessary memory. + * 6) update the set's size. + */ + +t_error set_remove_array(i_set setid, + t_id id) +{ + o_set* o; + t_setsz i; + t_id current; + + /* + * 0) + */ + + if (id == ID_UNUSED) + CORE_ESCAPE("invalid object identifier"); + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + if (o->size == 0) + CORE_ESCAPE("the set is empty"); + + /* + * 3) + */ + + for (i = 0; i < o->u.array.arraysz; ++i) + { + if (o->u.array.array[i]) + { + current = *((t_id*)(o->u.array.array[i])); + + if ((o->options & SET_OPTION_SORT) && current > id) + CORE_ESCAPE("unable to locate the given object"); + + if (current == id) + { + if ((o->options & SET_OPTION_FREE) || + (o->options & SET_OPTION_ALLOCATE)) + free(o->u.array.array[i]); + + o->u.array.array[i] = NULL; + + break; + } + } + } + + /* + * 4) + */ + + if (i == o->u.array.arraysz) + CORE_ESCAPE("unable to locate the given object"); + + /* + * 5) + */ + + if (o->options & SET_OPTION_ORGANISE) + { + for (; i < o->u.array.arraysz - 1; ++i) + { + o->u.array.array[i] = o->u.array.array[i + 1]; + } + + --o->u.array.arraysz; + + if ((o->u.array.array = realloc(o->u.array.array, + o->u.array.arraysz * + sizeof (void*))) == NULL) + CORE_ESCAPE("unable to reallocate memory for the array"); + } + + /* + * 6) + */ + + --o->size; + + CORE_LEAVE(); +} + +/* + * this function deletes an element given an iterator. + * + * steps: + * + * 1) retrieve the set descriptor. + * 2) save the iterator's index. + * 3) if the iterator is out of bound, return an error. + * 4) if the slot is unused, return an error. + * 5) if required, free the object and re-initialize its slot + * as being available. + * 6) if required, re-organise the array. + * 7) update the set's size. + */ + +t_error set_delete_array(i_set setid, + s_iterator iterator) +{ + o_set* o; + t_setsz i; + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + i = iterator.u.array.i; + + /* + * 3) + */ + + if (i >= o->u.array.arraysz) + CORE_ESCAPE("the given iterator is out of bound"); + + /* + * 4) + */ + + if (o->u.array.array[i] == NULL) + CORE_ESCAPE("the object the iterator points to does not exist"); + + /* + * 5) + */ + + if (o->options & SET_OPTION_ALLOCATE || + o->options & SET_OPTION_FREE) + free(o->u.array.array[i]); + + o->u.array.array[i] = NULL; + + /* + * 6) + */ + + if (o->options & SET_OPTION_ORGANISE) + { + for (; i < o->u.array.arraysz - 1; ++i) + { + o->u.array.array[i] = o->u.array.array[i + 1]; + } + + --o->u.array.arraysz; + + if ((o->u.array.array = realloc(o->u.array.array, + o->u.array.arraysz * + sizeof (void*))) == NULL) + CORE_ESCAPE("unable to reallocate memory for the array"); + } + + /* + * 7) + */ + + o->size--; + + CORE_LEAVE(); +} + +/* + * this function flushes the set, releasing every element. + * + * steps: + * + * 1) retrieve the set descriptor. + * 2) if required, free the memory of every object. + * 3) reset the array to its original size and mark every slot as + * being unused. + * 4) set the size of the set to zero. + */ + +t_error set_flush_array(i_set setid) +{ + o_set* o; + t_setsz i; + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + if ((o->options & SET_OPTION_FREE) || + (o->options & SET_OPTION_ALLOCATE)) + { + for (i = 0; i < o->u.array.arraysz; ++i) + { + if (o->u.array.array[i]) + { + free(o->u.array.array[i]); + + o->u.array.array[i] = NULL; + } + } + + o->size = 0; + } + + /* + * 3) + */ + + if ((o->u.array.array = realloc(o->u.array.array, + o->u.array.initsz * sizeof (void*))) == NULL) + CORE_ESCAPE("unable to reallocate memory for the array"); + + for (i = 0; i < o->u.array.initsz; ++i) + { + o->u.array.array[i] = NULL; + } + + o->u.array.arraysz = o->u.array.initsz; + + /* + * 4) + */ + + o->size = 0; + + CORE_LEAVE(); +} + +/* + * this function tries to locate an object according to its identifier + * and build a corresponding iterator. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) go through the elements and locate the slot. + * 3) if not found, return an error. + */ + +t_error set_locate_array(i_set setid, + t_id id, + s_iterator* iterator) +{ + o_set* o; + i_set i; + + /* + * 0) + */ + + if (iterator == NULL) + CORE_ESCAPE("the 'iterator' argument is null"); + + if (id == ID_UNUSED) + CORE_ESCAPE("invalid object identifier"); + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + for (i = 0; i < o->u.array.arraysz; ++i) + { + if (o->u.array.array[i] && + *((t_id*)(o->u.array.array[i])) == id) + { + iterator->u.array.i = i; + + CORE_LEAVE(); + } + } + + /* + * 3) + */ + + CORE_ESCAPE("unable to locate the given identifier in the set"); +} + +/* + * this function returns an object given its iterator. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) if the iterator is out of bound, return an error. + * 3) if the slot the iterator points to is unused, return an error. + * 4) return the object. + */ + +t_error set_object_array(i_set setid, + s_iterator iterator, + void** data) +{ + o_set* o; + + /* + * 0) + */ + + if (data == NULL) + CORE_ESCAPE("the 'data' argument is null"); + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + if (iterator.u.array.i >= o->u.array.arraysz) + CORE_ESCAPE("the given iterator is out of bound"); + + /* + * 3) + */ + + if (o->u.array.array[iterator.u.array.i] == NULL) + CORE_ESCAPE("the object the iterator points to does not exist"); + + /* + * 4) + */ + + *data = o->u.array.array[iterator.u.array.i]; + + CORE_LEAVE(); +} + +/* + * this function reserves a set according to several options, the initial + * number of elements in the array and the size of every element. + * + * steps: + * + * 0) verify the arguments. + * 1) assign an identifier, depending on the options: either this is + * the set container or not. if it is, take the set container identifier + * that is in the set manager. + * 2) initialize and fill the set descriptor. + * 3) allocate and initialize the array data structure. + * 4) register the set descriptor. + */ + +t_error set_reserve_array(t_options options, + t_setsz initsz, + t_size datasz, + i_set* id) +{ + o_set o; + t_setsz i; + + /* + * 0) + */ + + if (datasz < sizeof (t_id)) + CORE_ESCAPE("unable to reserve a set for objects smaller than " + "an identifier"); + + if (id == NULL) + CORE_ESCAPE("the 'id' argument is null"); + + if ((options & SET_OPTION_ALLOCATE) && (options & SET_OPTION_FREE)) + CORE_ESCAPE("unable to reserve a set with both alloc and free options"); + + /* + * 1) + */ + + if (options & SET_OPTION_CONTAINER) + { + *id = _set.sets; + } + else + { + if (id_reserve(&_set.id, id) != ERROR_OK) + CORE_ESCAPE("unable to reserve an identifier"); + } + + /* + * 2) + */ + + memset(&o, 0x0, sizeof (o_set)); + + o.id = *id; + o.size = 0; + o.type = SET_TYPE_ARRAY; + o.options = options; + o.datasz = datasz; + + o.u.array.arraysz = (initsz == 0 ? 1 : initsz); + o.u.array.initsz = o.u.array.arraysz; + + /* + * 3) + */ + + if ((o.u.array.array = malloc(o.u.array.arraysz * sizeof (void*))) == NULL) + { + if (!(options & SET_OPTION_CONTAINER)) + id_release(&_set.id, o.id); + + CORE_ESCAPE("unable to allocate memory for the array"); + } + + for (i = 0; i < o.u.array.arraysz; i++) + o.u.array.array[i] = NULL; + + /* + * 4) + */ + + if (set_new(&o) != ERROR_OK) + { + free(o.u.array.array); + + if (!(options & SET_OPTION_CONTAINER)) + id_release(&_set.id, o.id); + + CORE_ESCAPE("unable to register the new set descriptor"); + } + + CORE_LEAVE(); +} + +/* + * this function releases a set. + * + * steps: + * + * 1) retrieve the set descriptor. + * 2) flush the set. + * 3) release the set identifier. + * 4) free the array data structure's memory. + * 5) remove the set descriptor from the set container. + */ + +t_error set_release_array(i_set setid) +{ + o_set* o; + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + if (set_flush(setid) != ERROR_OK) + CORE_ESCAPE("unable to flush the set"); + + /* + * 3) + */ + + if (id_release(&_set.id, o->id) != ERROR_OK) + CORE_ESCAPE("unable to release the set's identifier"); + + /* + * 4) + */ + + free(o->u.array.array); + + /* + * 5) + */ + + if (set_destroy(o->id) != ERROR_OK) + CORE_ESCAPE("unable to destroy the set descriptor"); + + CORE_LEAVE(); +} + +/* + * this function just returns an error because the array set does not + * support this operation. + */ + +t_error set_push_array(i_set setid, + void* data) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} + +/* + * this function just returns an error because the array set does not + * support this operation. + */ + +t_error set_pop_array(i_set setid) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} + +/* + * this function just returns an error because the array set does not + * support this operation. + */ + +t_error set_pick_array(i_set setid, + void** data) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} diff --git a/kaneton/core/set/set-bpt.c b/kaneton/core/set/set-bpt.c new file mode 100644 index 0000000..74d30fd --- /dev/null +++ b/kaneton/core/set/set-bpt.c @@ -0,0 +1,1345 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/set/set-bpt.c + * + * created julien quintard [fri feb 11 03:04:40 2005] + * updated julien quintard [sun jan 30 20:35:34 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this set implementation provides a balanced+ tree data structure. + * + * this set type is build over the header file bpt.h developed by julien + * quintard. this header file was developed to provide an easy way to build, + * manage and tune balanced+ trees in C. + * + * note that the SORT option must be provided because a non-sorted trees + * are not conceivable. besides the 'datasz' argument of the set_reserve() + * function is needed only if the ALLOCATE option is activated. + * + * options: + * SET_OPTION_CONTAINER + * SET_OPTION_SORT + * SET_OPTION_ALLOCATE + * SET_OPTION_FREE + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the set manager's structure. + */ + +extern m_set _set; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this macro-function call generates the bpt functions. + */ + +bpt_make_functions(set, id, data); + +/* + * this function loads a bpt node from main memory to main memory. + * + * in other words this function does nothing but to set the node's attributes. + */ + +void set_load_bpt(t_bpt(set)* bpt, + t_bpt_imm(set)* node, + t_bpt_node(set) addr) +{ + node->addr = addr; + node->buf = (void*)addr; +} + +/* + * this function unloads a node. + * + * since nodes are stored in main memory, this function has nothing to do. + */ + +void set_unload_bpt(t_bpt(set)* bpt, + t_bpt_imm(set)* node) +{ +} + +/* + * this function compares two bpt addresses. + */ + +int set_addrcmp_bpt(t_bpt(set)* bpt, + t_bpt_addr(set) addr1, + t_bpt_addr(set) addr2) +{ + if (addr1 < addr2) + return (-1); + + if (addr1 > addr2) + return (1); + + return (0); +} + +/* + * this function compares two bpt keys. + */ + +int set_keycmp_bpt(t_bpt(set)* bpt, + t_bpt_key(set) key1, + t_bpt_key(set) key2) +{ + if (key1 < key2) + return (-1); + + if (key1 > key2) + return (1); + + return (0); +} + +/* + * this function compares two bpt values. + */ + +int set_valcmp_bpt(t_bpt(set)* bpt, + t_bpt_value(set) value1, + t_bpt_value(set) value2) +{ + if (value1 < value2) + return (-1); + + if (value1 > value2) + return (1); + + return (0); +} + +/* + * this function returns true if the object is present in the set. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) search for the identifier in the tree and return false if not found. + */ + +t_error set_exist_bpt(i_set setid, + t_id id) +{ + t_bpt_entry(set) entry; + o_set* o; + + /* + * 0) + */ + + assert(id != ID_UNUSED); + + /* + * 1) + */ + + assert(set_descriptor(setid, &o) == ERROR_OK); + + /* + * 2) + */ + + if (bpt_search(set, &o->u.bpt.bpt, id, &entry) != 0) + CORE_FALSE(); + + CORE_TRUE(); +} + +/* + * this function builds the initial version of the array of unused blocks. + * + * steps: + * + * 0) verify the arguments. + * 1) initialize the array's size in order to initialize (init operation) + * the tree. + * 2) allocate the array. + * 3) initialize the array's content. + * 4) fill the required number of entries with blocks allocated in + * main memory. + */ + +t_error set_build_bpt(o_set* object, + BPT_NODESZ_T nodesz) +{ + /* + * 0) + */ + + if (object == NULL) + CORE_ESCAPE("the 'object' argument is null"); + + /* + * 1) + */ + + object->u.bpt.unusedsz = BPT_INIT_SIZE(); + + /* + * 2) + */ + + if ((object->u.bpt.unused.array = malloc(object->u.bpt.unusedsz * + sizeof (t_bpt_addr(set)))) == NULL) + CORE_ESCAPE("unable to allocate memory for the array of unused blocks"); + + /* + * 3) + */ + + memset(object->u.bpt.unused.array, 0x0, + object->u.bpt.unusedsz * sizeof (t_bpt_addr(set))); + + object->u.bpt.unused.index = -1; + + /* + * 4) + */ + + for (; + (object->u.bpt.unused.index + 1) < object->u.bpt.unusedsz; + object->u.bpt.unused.index++) + { + if ((object->u.bpt.unused.array[(object->u.bpt.unused.index + 1)] = + malloc(nodesz)) == NULL) + CORE_ESCAPE("unable to allocate memory for the unused block"); + } + + CORE_LEAVE(); +} + +/* + * this function adjusts the size and content of the array of unused + * blocks, hence possibly allocating additional blocks or releasing others. + * + * steps: + * + * 0) verify the arguments. + * 1) if the size of the current array is not large enough, resize it. + * 2) if there is not enough elements in the array, fill it with + * additional ones. + */ + +t_error set_adjust_bpt(o_set* object, + t_bpt_uni(set) alloc, + t_bpt_uni(set) size) +{ + /* + * 0) + */ + + if (object == NULL) + CORE_ESCAPE("the 'object' argument is null"); + + /* + * 1) + */ + + if (object->u.bpt.unusedsz < size) + { + if ((object->u.bpt.unused.array = + realloc(object->u.bpt.unused.array, size * + sizeof (t_bpt_addr(set)))) == NULL) + CORE_ESCAPE("unable to reallocate memory for the array of unused " + "blocks"); + + object->u.bpt.unusedsz = size; + } + + /* + * 2) + */ + + if ((object->u.bpt.unused.index + 1) > alloc) + { + for (; + (object->u.bpt.unused.index + 1) > alloc; + object->u.bpt.unused.index--) + { + free(object->u.bpt.unused.array[object->u.bpt.unused.index]); + + object->u.bpt.unused.array[object->u.bpt.unused.index] = + SET_BPT_UADDR; + } + } + else + { + for (; + (object->u.bpt.unused.index + 1) < alloc; + object->u.bpt.unused.index++) + { + if ((object->u.bpt.unused.array[(object->u.bpt.unused.index + 1)] = + malloc(object->u.bpt.bpt.nodesz)) == NULL) + CORE_ESCAPE("unable to allocate memory for the unused block"); + } + } + + CORE_LEAVE(); +} + +/* + * this function destroys an array of unused blocks. + * + * steps: + * + * 0) verify the arguments. + * 1) free each block from the array. + * 2) free the array itself. + */ + +t_error set_destroy_bpt(o_set* object) +{ + t_bpt_uni(set) i; + + /* + * 0) + */ + + if (object == NULL) + CORE_ESCAPE("the 'object' argument is null"); + + /* + * 1) + */ + + for (i = 0; i <= object->u.bpt.unused.index; i++) + free(object->u.bpt.unused.array[i]); + + /* + * 2) + */ + + free(object->u.bpt.unused.array); + + CORE_LEAVE(); +} + +/* + * this function shows the set's attributes. + * + * steps: + * + * 1) gets the descriptor from its set identifier. + * 2) prints a message for each objects of the set. + */ + +t_error set_show_bpt(i_set setid, + mt_margin margin) +{ + char options[5]; + t_state state; + o_set* o; + s_iterator i; + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + if (o->options & SET_OPTION_CONTAINER) + options[0] = 'c'; + else + options[0] = '.'; + + if (o->options & SET_OPTION_SORT) + options[1] = 's'; + else + options[1] = '.'; + + if (o->options & SET_OPTION_ALLOCATE) + options[2] = 'a'; + else + options[2] = '.'; + + if (o->options & SET_OPTION_FREE) + options[3] = 'f'; + else + options[3] = '.'; + + options[4] = '\0'; + + /* + * 3) + */ + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + "set: id(%qd) type(bpt) size(%qd) datasz(%u) options(%s) " + "array(0x%x:%u) arraysz(%d)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + o->id, + o->size, + o->datasz, + options, + o->u.bpt.unused.array, + o->u.bpt.unused.index, + o->u.bpt.unusedsz); + + /* + * 4) + */ + + set_foreach(SET_OPTION_FORWARD, setid, &i, state) + { + t_bpt_imm(set) node; + t_bpt_lfentry(set)* leaf; + + BPT_LOAD(&o->u.bpt.bpt, &node, i.u.bpt.entry.node); + + leaf = BPT_LFENTRY(set, &node, i.u.bpt.entry.ndi); + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " object: id(%qd) entry(0x%x:%u) data(0x%x)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + leaf->id, + i.u.bpt.entry.node, + i.u.bpt.entry.ndi, + leaf->data); + + BPT_UNLOAD(&o->u.bpt.bpt, &node); + } + + CORE_LEAVE(); +} + +/* + * this function returns an iterator on the first object. + * + * this function relies on th bpt_list() function with the BPT_OPT_HEAD option + * to walk through the objects in a sequentially manner. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) load the node. + * 3) look for the first entry and return true if found. + * 4) unload the node. + */ + +t_error set_head_bpt(i_set setid, + s_iterator* iterator) +{ + t_bpt_imm(set) root; + o_set* o; + + /* + * 0) + */ + + assert(iterator != NULL); + + /* + * 1) + */ + + assert(set_descriptor(setid, &o) == ERROR_OK); + + /* + * 2) + */ + + BPT_LOAD(&o->u.bpt.bpt, &root, o->u.bpt.bpt.root); + + /* + * 3) + */ + + if (bpt_list(set, &o->u.bpt.bpt, &root, + &iterator->u.bpt.entry, BPT_OPT_HEAD) != 0) + { + BPT_UNLOAD(&o->u.bpt.bpt, &root); + + CORE_FALSE(); + } + + /* + * 4) + */ + + BPT_UNLOAD(&o->u.bpt.bpt, &root); + + CORE_TRUE(); +} + +/* + * this function looks for the last entry. + * + * this function relies on th bpt_list() function with the BPT_OPT_TAIL option + * to walk through the objects in a sequentially manner. + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) load the node. + * 3) look for the first entry and return true if found. + * 4) unload the node. + */ + +t_error set_tail_bpt(i_set setid, + s_iterator* iterator) +{ + t_bpt_imm(set) root; + o_set* o; + + /* + * 0) + */ + + assert(iterator != NULL); + + /* + * 1) + */ + + assert(set_descriptor(setid, &o) == ERROR_OK); + + /* + * 2) + */ + + BPT_LOAD(&o->u.bpt.bpt, &root, o->u.bpt.bpt.root); + + /* + * 3) + */ + + if (bpt_list(set, &o->u.bpt.bpt, &root, + &iterator->u.bpt.entry, BPT_OPT_TAIL) != 0) + { + BPT_UNLOAD(&o->u.bpt.bpt, &root); + + CORE_FALSE(); + } + + /* + * 4) + */ + + BPT_UNLOAD(&o->u.bpt.bpt, &root); + + CORE_TRUE(); +} + +/* + * this function returns an iterator on the previous node. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) retrieve the previous entry. + */ + +t_error set_previous_bpt(i_set setid, + s_iterator current, + s_iterator* previous) +{ + o_set* o; + + /* + * 0) + */ + + assert(previous != NULL); + + /* + * 1) + */ + + assert(set_descriptor(setid, &o) == ERROR_OK); + + /* + * 2) + */ + + if (bpt_prev_entry(set, &o->u.bpt.bpt, current.u.bpt.entry, + &previous->u.bpt.entry, BPT_OPT_TREE) != 0) + CORE_FALSE(); + + CORE_TRUE(); +} + +/* + * this function returns an iterator on the next node. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) retrieve the previous entry. + */ + +t_error set_next_bpt(i_set setid, + s_iterator current, + s_iterator* next) +{ + o_set* o; + + /* + * 0) + */ + + assert(next != NULL); + + /* + * 1) + */ + + assert(set_descriptor(setid, &o) == ERROR_OK); + + /* + * 2) + */ + + if (bpt_next_entry(set, &o->u.bpt.bpt, current.u.bpt.entry, + &next->u.bpt.entry, BPT_OPT_TREE) != 0) + CORE_FALSE(); + + CORE_TRUE(); +} + +/* + * this function just returns an error because the bpt set only + * works with the sort option. + */ + +t_error set_insert_bpt(i_set setid, + void* data) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} + +/* + * this function just returns an error because the bpt set only + * works with the sort option. + */ + +t_error set_append_bpt(i_set setid, + void* data) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} + +/* + * this function just returns an error because the bpt set only + * works with the sort option. + */ + +t_error set_before_bpt(i_set setid, + s_iterator iterator, + void* data) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} + +/* + * this function just returns an error because the bpt set only + * works with the sort option. + */ + +t_error set_after_bpt(i_set setid, + s_iterator iterator, + void* data) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} + +/* + * this function adds the given object to the tree. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) initialize the leaf entry. + * 3) complete the leaf entry depending on the options i.e clone the + * object if necessary. + * 4) adjust the size and filling of the array of unused blocks. + * 5) add the entry to the tree. + * 6) update the set's size. + */ + +t_error set_add_bpt(i_set setid, + void* data) +{ + t_bpt_lfentry(set) lfentry; + o_set* o; + + /* + * 0) + */ + + if (data == NULL) + CORE_ESCAPE("the 'data' argument is null"); + + if (*(t_id*)data == ID_UNUSED) + CORE_ESCAPE("the object must begin with a valid identifier"); + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + memset(&lfentry, 0x0, sizeof (t_bpt_lfentry(set))); + + lfentry.id = *((t_id*)data); + + /* + * 3) + */ + + if (o->options & SET_OPTION_ALLOCATE) + { + if ((lfentry.data = malloc(o->datasz)) == NULL) + CORE_ESCAPE("unable to allocate memory for the object's copy"); + + memcpy(lfentry.data, data, o->datasz); + } + else + { + lfentry.data = data; + } + + /* + * 4) + */ + + if (set_adjust_bpt(o, + BPT_ADD_ALLOC(&o->u.bpt.bpt), + BPT_ADD_SIZE(&o->u.bpt.bpt)) != ERROR_OK) + CORE_ESCAPE("unable to adjust the array of unused blocks"); + + /* + * 5) + */ + + if (bpt_add(set, &o->u.bpt.bpt, &lfentry, &o->u.bpt.unused) != 0) + CORE_ESCAPE("unable to add the object to the tree"); + + /* + * 6) + */ + + o->size++; + + CORE_LEAVE(); +} + +/* + * this function removes an object from the tree. + * + * note that the leaf entry may have to be retrieved in order to release + * the object's memory. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) depending on the options, retrieve the entry and release the object's + * memory. + * 3) adjust the size and filling of the array of unused blocks. + * 4) remove the object from the tree. + * 5) update the set's size. + */ + +t_error set_remove_bpt(i_set setid, + t_id id) +{ + t_bpt_entry(set) entry; + t_bpt_imm(set) node; + o_set* o; + + /* + * 0) + */ + + if (id == ID_UNUSED) + CORE_ESCAPE("invalid object identifier"); + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + if ((o->options & SET_OPTION_ALLOCATE) || + (o->options & SET_OPTION_FREE)) + { + if (bpt_search(set, &o->u.bpt.bpt, id, &entry) != 0) + CORE_ESCAPE("unable to locate the given object"); + + BPT_LOAD(&o->u.bpt.bpt, &node, entry.node); + + free(BPT_GET_LFENTRY(set, &node, entry.ndi, data)); + + BPT_UNLOAD(&o->u.bpt.bpt, &node); + } + + /* + * 3) + */ + + if (set_adjust_bpt(o, + BPT_REMOVE_ALLOC(&o->u.bpt.bpt), + BPT_REMOVE_SIZE(&o->u.bpt.bpt)) != ERROR_OK) + CORE_ESCAPE("unable to adjust the array of unused blocks"); + + /* + * 4) + */ + + if (bpt_remove(set, &o->u.bpt.bpt, id, &o->u.bpt.unused) != 0) + CORE_ESCAPE("unable to remove the object from the tree"); + + /* + * 5) + */ + + o->size--; + + CORE_LEAVE(); +} + +/* + * this function deletes the object referenced by the given iterator. + * + * steps: + * + * 1) retrieve the set descriptor. + * 2) if necessary, retrieve the object and release its memory. + * 3) adjusts the size and filling of the array of unused blocks. + * 4) removes the object from the tree. + * 5) update the set's size. + */ + +t_error set_delete_bpt(i_set setid, + s_iterator iterator) +{ + t_bpt_imm(set) node; + o_set* o; + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + if (o->options & SET_OPTION_ALLOCATE || + o->options & SET_OPTION_FREE) + { + BPT_LOAD(&o->u.bpt.bpt, &node, iterator.u.bpt.entry.node); + + free(BPT_GET_LFENTRY(set, &node, iterator.u.bpt.entry.ndi, data)); + + BPT_UNLOAD(&o->u.bpt.bpt, &node); + } + + /* + * 3) + */ + + if (set_adjust_bpt(o, + BPT_REMOVE_ALLOC(&o->u.bpt.bpt), + BPT_REMOVE_SIZE(&o->u.bpt.bpt)) != ERROR_OK) + CORE_ESCAPE("unable to adjust the array of unused blocks"); + + /* + * 4) + */ + + if (bpt_collide_remove(set, &o->u.bpt.bpt, iterator.u.bpt.entry, + &o->u.bpt.unused) != 0) + CORE_ESCAPE("unable to remove the object from the tree"); + + /* + * 5) + */ + + o->size--; + + CORE_LEAVE(); +} + +/* + * this function flushes the contents of the tree. + * + * this action results in the destruction of the bpt internal data structure. + * therefore, the function ends by calling the bpt_init() function in order + * to rebuild the tree data structure. + * + * steps: + * + * 1) retrieve the set descriptor. + * 2) save the size of the nodes from the current tree configuration. + * 3) if necessary, walk through the objects and release their memory. + * 4) adjust the size and filling of the array of unused objects and + * clean the tree data structure. + * 5) re-adjusts the array of unused blocks for the next operation and + * re-initialize the tree. + * 6) resets the set's size to zero. + */ + +t_error set_flush_bpt(i_set setid) +{ + t_bpt_nodesz(set) nodesz; + t_state state; + s_iterator i; + o_set* o; + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + nodesz = o->u.bpt.bpt.nodesz; + + /* + * 3) + */ + + if ((o->options & SET_OPTION_ALLOCATE) || + (o->options & SET_OPTION_FREE)) + { + set_foreach(SET_OPTION_FORWARD, setid, &i, state) + { + t_bpt_imm(set) node; + t_bpt_lfentry(set)* leaf; + + BPT_LOAD(&o->u.bpt.bpt, &node, i.u.bpt.entry.node); + + leaf = BPT_LFENTRY(set, &node, i.u.bpt.entry.ndi); + + free(leaf->data); + + leaf->data = SET_BPT_UADDR; + + BPT_UNLOAD(&o->u.bpt.bpt, &node); + } + } + + /* + * 4) + */ + + if (set_adjust_bpt(o, + BPT_CLEAN_ALLOC(&o->u.bpt.bpt), + BPT_CLEAN_SIZE(&o->u.bpt.bpt)) != ERROR_OK) + CORE_ESCAPE("unable to adjust the array of unused blocks"); + + if (bpt_clean(set, &o->u.bpt.bpt, &o->u.bpt.unused) != 0) + CORE_ESCAPE("unable to clean the tree of all the elements"); + + /* + * 5) + */ + + if (set_adjust_bpt(o, + BPT_INIT_ALLOC(), + BPT_INIT_SIZE()) != ERROR_OK) + CORE_ESCAPE("unable to adjust the array of unused blocks"); + + if (bpt_init(set, &o->u.bpt.bpt, nodesz, + SET_BPT_UADDR, SET_BPT_UKEY, SET_BPT_UVALUE, + BPT_FLAG_ZERO, set_load_bpt, set_unload_bpt, + set_addrcmp_bpt, set_keycmp_bpt, set_valcmp_bpt, + NULL, NULL, &o->u.bpt.unused) != 0) + CORE_ESCAPE("unable to re-initialize the tree"); + + /* + * 6) + */ + + o->size = 0; + + CORE_LEAVE(); +} + +/* + * this function locates the leaf entry corresponding to the given + * identifier, then returns an iterator for it. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) search the entry with the given identifier. + * 3) set the iterator. + */ + +t_error set_locate_bpt(i_set setid, + t_id id, + s_iterator* iterator) +{ + t_bpt_entry(set) entry; + o_set* o; + + /* + * 0) + */ + + if (iterator == NULL) + CORE_ESCAPE("the 'iterator' argument is null"); + + if (id == ID_UNUSED) + CORE_ESCAPE("invalid object identifier"); + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + if (bpt_search(set, &o->u.bpt.bpt, id, &entry) != 0) + CORE_ESCAPE("unable to locate the element in the tree"); + + /* + * 3) + */ + + memcpy(&iterator->u.bpt.entry, &entry, sizeof (t_bpt_entry(set))); + + CORE_LEAVE(); +} + +/* + * this function returns the object from the given iterator. + * + * steps: + * + * 1) verify the arguments. + * 1) retrieve the set descriptor. + * 2) load the node. + * 3) retrieve the data from the node's entry. + * 4) unload the node. + */ + +t_error set_object_bpt(i_set setid, + s_iterator iterator, + void** data) +{ + t_bpt_imm(set) node; + o_set* o; + + /* + * 0) + */ + + if (data == NULL) + CORE_ESCAPE("the 'data' argument is null"); + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + BPT_LOAD(&o->u.bpt.bpt, &node, iterator.u.bpt.entry.node); + + /* + * 3) + */ + + *data = BPT_GET_LFENTRY(set, &node, iterator.u.bpt.entry.ndi, data); + + /* + * 4) + */ + + BPT_UNLOAD(&o->u.bpt.bpt, &node); + + CORE_LEAVE(); +} + +/* + * this function reserves a set according to the given options. + * + * note that while the 'datasz' argument specifies the size of the objects + * to be stored, the 'nodesz' indicates the size of the internal and leaf + * nodes. + * + * steps: + * + * 0) verify the arguments. + * 1) assign an identifier, depending on the options: either this is + * the set container or not. if it is, take the set container identifier + * that is in the set manager. + * 2) initialize and fill the set descriptor. + * 3) build the initial array of unused blocks with enough filling to + * satisfy the bpt_init() operation. + * 4) set up the tree data structure. + * 5) register the set descriptor. + */ + +t_error set_reserve_bpt(t_options options, + t_size datasz, + t_bpt_nodesz(set) nodesz, + i_set* id) +{ + o_set o; + + /* + * 0) + */ + + if (datasz < sizeof (t_id)) + CORE_ESCAPE("unable to reserve a set for objects smaller than " + "an identifier"); + + if (id == NULL) + CORE_ESCAPE("the 'id' argument is null"); + + if (!(options & SET_OPTION_SORT)) + CORE_ESCAPE("the sort option cannot be provided since bpt-based are " + "always sorted"); + + if (options & SET_OPTION_ORGANISE) + CORE_ESCAPE("the organise option is invalid for bpt-based sets"); + + if ((options & SET_OPTION_ALLOCATE) && (options & SET_OPTION_FREE)) + CORE_ESCAPE("unable to reserve a set with both alloc and free options"); + + /* + * 1) + */ + + if (options & SET_OPTION_CONTAINER) + { + *id = _set.sets; + } + else + { + if (id_reserve(&_set.id, id) != ERROR_OK) + CORE_ESCAPE("unable to reserve an identifier"); + } + + /* + * 2) + */ + + memset(&o, 0x0, sizeof (o_set)); + + o.id = *id; + o.size = 0; + o.type = SET_TYPE_BPT; + o.options = options; + o.datasz = datasz; + + /* + * 3) + */ + + if (set_build_bpt(&o, nodesz) != ERROR_OK) + { + set_destroy_bpt(&o); + + if (!(options & SET_OPTION_CONTAINER)) + id_release(&_set.id, o.id); + + CORE_ESCAPE("unable to build the array of unused blocks"); + } + + /* + * 4) + */ + + if (bpt_init(set, &o.u.bpt.bpt, nodesz, + SET_BPT_UADDR, SET_BPT_UKEY, SET_BPT_UVALUE, + BPT_FLAG_ZERO, set_load_bpt, set_unload_bpt, + set_addrcmp_bpt, set_keycmp_bpt, set_valcmp_bpt, + NULL, NULL, &o.u.bpt.unused) != 0) + { + if (!(options & SET_OPTION_CONTAINER)) + id_release(&_set.id, o.id); + + CORE_ESCAPE("unable to initialize the tree"); + } + + /* + * 5) + */ + + if (set_new(&o) != ERROR_OK) + { + set_adjust_bpt(&o, + BPT_CLEAN_ALLOC(&o.u.bpt.bpt), + BPT_CLEAN_SIZE(&o.u.bpt.bpt)); + + bpt_clean(set, &o.u.bpt.bpt, &o.u.bpt.unused); + + set_destroy_bpt(&o); + + if (!(options & SET_OPTION_CONTAINER)) + id_release(&_set.id, o.id); + + CORE_ESCAPE("unable to register the new set descriptor"); + } + + CORE_LEAVE(); +} + +/* + * this function releases the set. + * + * steps: + * + * 1) retrieve the set descriptor. + * 2) flush the set's content. + * 3) adjust the size and filling of the array of unused blocks and + * clean the tree data structure. + * 4) destroy the array of unused blocks. + * 5) release the set identifier. + * 6) remove the set descriptor from the set container. + */ + +t_error set_release_bpt(i_set setid) +{ + o_set *o; + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + if (set_flush(setid) != ERROR_OK) + CORE_ESCAPE("unable to flush the set"); + + /* + * 3) + */ + + if (set_adjust_bpt(o, + BPT_CLEAN_ALLOC(&o->u.bpt.bpt), + BPT_CLEAN_SIZE(&o->u.bpt.bpt)) != ERROR_OK) + CORE_ESCAPE("unable to adjust the array of unused blocks"); + + if (bpt_clean(set, &o->u.bpt.bpt, &o->u.bpt.unused) != 0) + CORE_ESCAPE("unable to clean the tree"); + + /* + * 4) + */ + + if (set_destroy_bpt(o) != ERROR_OK) + CORE_ESCAPE("unable to destroy the set"); + + /* + * 5) + */ + + if (id_release(&_set.id, o->id) != ERROR_OK) + CORE_ESCAPE("unable to release the set identifier"); + + /* + * 6) + */ + + if (set_destroy(o->id) != ERROR_OK) + CORE_ESCAPE("unable to destroy the set descriptor"); + + CORE_LEAVE(); +} + +/* + * this function just returns an error because the bpt set only + * works with the sort option. + */ + +t_error set_push_bpt(i_set setid, + void* data) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} + +/* + * this function just returns an error because the bpt set only + * works with the sort option. + */ + +t_error set_pop_bpt(i_set setid) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} + +/* + * this function just returns an error because the bpt set only + * works with the sort option. + */ + +t_error set_pick_bpt(i_set setid, + void** data) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} diff --git a/kaneton/core/set/set-ll.c b/kaneton/core/set/set-ll.c new file mode 100644 index 0000000..a34942a --- /dev/null +++ b/kaneton/core/set/set-ll.c @@ -0,0 +1,1434 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/set/set-ll.c + * + * created julien quintard [fri feb 11 03:04:40 2005] + * updated julien quintard [sun jan 30 20:34:59 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this set implementation provides a linked-list data structure. note that + * this data structure is in fact a doubly linked-list. + * + * as for the 'array' implementation, this type of set can be used in + * two ways. either objects are cloned and stored, in which case the + * ALLOCATE option is required, or pointers to objects are stored directly, + * implying that the objects have been allocated beforehand. + * + * note that the 'datasz' argument of the set_reserve() function is + * meaningful only if the ALLOCATE option is set. + * + * options: + * SET_OPTION_CONTAINER + * SET_OPTION_SORT + * SET_OPTION_ALLOCATE + * SET_OPTION_FREE + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the set manager. + */ + +extern m_set _set; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function returns true if the given identifier is registered in + * the set. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) walk through the objects and try to locate the object, otherwise + * return false. + */ + +t_error set_exist_ll(i_set setid, + t_id id) +{ + s_set_ll_node* tmp; + o_set* o; + + /* + * 0) + */ + + assert(id != ID_UNUSED); + + /* + * 1) + */ + + assert(set_descriptor(setid, &o) == ERROR_OK); + + /* + * 2) + */ + + for (tmp = o->u.ll.head; tmp != NULL; tmp = tmp->next) + { + if (*((t_id*)tmp->data) == id) + CORE_TRUE(); + } + + CORE_FALSE(); +} + +/* + * this function shows a set's attributes. + * + * steps: + * + * 1) retrieve the set descriptor. + * 2) build the options string. + * 3) display general information. + * 4) go through the objects and display them. + */ + +t_error set_show_ll(i_set setid, + mt_margin margin) +{ + char options[5]; + t_state state; + o_set* o; + s_iterator i; + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + if (o->options & SET_OPTION_CONTAINER) + options[0] = 'c'; + else + options[0] = '.'; + + if (o->options & SET_OPTION_SORT) + options[1] = 's'; + else + options[1] = '.'; + + if (o->options & SET_OPTION_ALLOCATE) + options[2] = 'a'; + else + options[2] = '.'; + + if (o->options & SET_OPTION_FREE) + options[3] = 'f'; + else + options[3] = '.'; + + options[4] = '\0'; + + /* + * 3) + */ + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + "set: id(%qd) type(ll) size(%qd) datasz(%u) options(%s) " + "head(0x%x) tail(0x%x)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + o->id, + o->size, + o->datasz, + options, + o->u.ll.head, + o->u.ll.tail); + + /* + * 4) + */ + + set_foreach(SET_OPTION_FORWARD, setid, &i, state) + { + s_set_ll_node* n = i.u.ll.node; + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " object: id(%qd) previous(0x%x) " + "current(0x%x) next(0x%x)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + *((t_id*)n->data), + n->previous, + n, + n->next); + } + + CORE_LEAVE(); +} + +/* + * this function returns an iterator on the first node of the list. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) if the set is empty, return false. + * 3) set the iterator. + */ + +t_error set_head_ll(i_set setid, + s_iterator* iterator) +{ + o_set* o; + + /* + * 0) + */ + + assert(iterator != NULL); + + /* + * 1) + */ + + assert(set_descriptor(setid, &o) == ERROR_OK); + + /* + * 2) + */ + + if (o->size == 0) + CORE_FALSE(); + + /* + * 3) + */ + + iterator->u.ll.node = o->u.ll.head; + + CORE_TRUE(); +} + +/* + * this function returns an iterator on the last node of the list. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) if the set is empty, return false. + * 3) set the iterator. + */ + +t_error set_tail_ll(i_set setid, + s_iterator* iterator) +{ + o_set* o; + + /* + * 0) + */ + + assert(iterator != NULL); + + /* + * 1) + */ + + assert(set_descriptor(setid, &o) == ERROR_OK); + + /* + * 2) + */ + + if (o->size == 0) + CORE_FALSE(); + + /* + * 3) + */ + + iterator->u.ll.node = o->u.ll.tail; + + CORE_TRUE(); +} + +/* + * this function returns an iterator on the previous node. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) if there is no previous object, return false. + * 3) set the iterator. + */ + +t_error set_previous_ll(i_set setid, + s_iterator current, + s_iterator* previous) +{ + s_set_ll_node* c = current.u.ll.node; + o_set* o; + + /* + * 0) + */ + + assert(previous != NULL); + + /* + * 1) + */ + + assert(set_descriptor(setid, &o) == ERROR_OK); + + /* + * 2) + */ + + if (c->previous == NULL) + CORE_FALSE(); + + /* + * 3) + */ + + previous->u.ll.node = c->previous; + + CORE_TRUE(); +} + +/* + * this function returns an iterator on the next node of the list. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) if there is no previous object, return false. + * 3) set the iterator. + */ + +t_error set_next_ll(i_set setid, + s_iterator current, + s_iterator* next) +{ + s_set_ll_node* c = current.u.ll.node; + o_set* o; + + /* + * 0) + */ + + assert(next != NULL); + + /* + * 1) + */ + + assert(set_descriptor(setid, &o) == ERROR_OK); + + /* + * 2) + */ + + if (c->next == NULL) + CORE_FALSE(); + + /* + * 3) + */ + + next->u.ll.node = c->next; + + CORE_TRUE(); +} + +/* + * this function inserts a new node at the head of the list. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) check that this operation is allowed for this set. + * 3) allocate and initialize the new node. + * 4) clone the object if required. + * 5) insert the new node and arrange the neighbour nodes. + * 6) update the set's size. + */ + +t_error set_insert_ll(i_set setid, + void* data) +{ + s_set_ll_node* n; + o_set* o; + + /* + * 0) + */ + + if (data == NULL) + CORE_ESCAPE("the 'data' argument is null"); + + if (*((t_id*)data) == ID_UNUSED) + CORE_ESCAPE("the provided object must begin with a valid identifier"); + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + if (o->options & SET_OPTION_SORT) + CORE_ESCAPE("unable to insert an object in a sorted set"); + + /* + * 3) + */ + + if ((n = malloc(sizeof (s_set_ll_node))) == NULL) + CORE_ESCAPE("unable to allocate memory for the linked-list node"); + + memset(n, 0x0, sizeof (s_set_ll_node)); + + /* + * 4) + */ + + if (o->options & SET_OPTION_ALLOCATE) + { + if ((n->data = malloc(o->datasz)) == NULL) + { + free(n); + + CORE_ESCAPE("unable to allocate memory for the object's copy"); + } + + memcpy(n->data, data, o->datasz); + } + else + { + n->data = data; + } + + /* + * 5) + */ + + n->previous = NULL; + n->next = o->u.ll.head; + + if (n->next != NULL) + n->next->previous = n; + + o->u.ll.head = n; + + if (o->u.ll.tail == NULL) + o->u.ll.tail = n; + + /* + * 6) + */ + + o->size++; + + CORE_LEAVE(); +} + +/* + * this function inserts a new node at the end of the list. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) check that this operation is allowed for this set. + * 3) allocate and initialize the new node. + * 4) clone the object if required. + * 5) insert the new node and arrange the neighbour nodes. + * 6) update the set's size. + */ + +t_error set_append_ll(i_set setid, + void* data) +{ + s_set_ll_node* n; + o_set* o; + + /* + * 0) + */ + + if (data == NULL) + CORE_ESCAPE("the 'data' argument is null"); + + if (*((t_id*)data) == ID_UNUSED) + CORE_ESCAPE("the provided object must begin with a valid identifier"); + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + if (o->options & SET_OPTION_SORT) + CORE_ESCAPE("unable to append an object to a sorted set"); + + /* + * 3) + */ + + if ((n = malloc(sizeof (s_set_ll_node))) == NULL) + CORE_ESCAPE("unable to allocate memory for the linked-list node"); + + memset(n, 0x0, sizeof (s_set_ll_node)); + + /* + * 4) + */ + + if (o->options & SET_OPTION_ALLOCATE) + { + if ((n->data = malloc(o->datasz)) == NULL) + { + free(n); + + CORE_ESCAPE("unable to allocate memory for the object's copy"); + } + + memcpy(n->data, data, o->datasz); + } + else + { + n->data = data; + } + + /* + * 5) + */ + + n->previous = o->u.ll.tail; + n->next = NULL; + + if (n->previous != NULL) + n->previous->next = n; + + o->u.ll.tail = n; + + if (o->u.ll.head == NULL) + o->u.ll.head = n; + + /* + * 6) + */ + + o->size++; + + CORE_LEAVE(); +} + +/* + * this function inserts an object before the one referenced by the iterator. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) check that this operation is allowed for this set. + * 3) allocate and initialize the new node. + * 4) clone the object if required. + * 5) insert the new node and arrange the neighbour nodes. + * 6) update the set's size. + */ + +t_error set_before_ll(i_set setid, + s_iterator iterator, + void* data) +{ + s_set_ll_node* i = iterator.u.ll.node; + s_set_ll_node* n; + o_set* o; + + /* + * 0) + */ + + if (data == NULL) + CORE_ESCAPE("the 'data' argument is null"); + + if (*((t_id*)data) == ID_UNUSED) + CORE_ESCAPE("the provided object must begin with a valid identifier"); + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + if (o->options & SET_OPTION_SORT) + CORE_ESCAPE("unable to insert at a precise location in a sorted set"); + + /* + * 3) + */ + + if ((n = malloc(sizeof (s_set_ll_node))) == NULL) + CORE_ESCAPE("unable to allocate memory for the linked-list node"); + + memset(n, 0x0, sizeof (s_set_ll_node)); + + /* + * 4) + */ + + if (o->options & SET_OPTION_ALLOCATE) + { + if ((n->data = malloc(o->datasz)) == NULL) + { + free(n); + + CORE_ESCAPE("unable to allocate memory for the object's copy"); + } + + memcpy(n->data, data, o->datasz); + } + else + { + n->data = data; + } + + /* + * 5) + */ + + n->previous = i->previous; + n->next = i; + + i->previous = n; + + if (n->previous != NULL) + n->previous->next = n; + else + o->u.ll.head = n; + + /* + * 6) + */ + + o->size++; + + CORE_LEAVE(); +} + +/* + * this function inserts a new object after a existing one referenced + * by the iterator. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) check that this operation is allowed for this set. + * 3) allocate and initialize the new node. + * 4) clone the object if required. + * 5) insert the new node and arrange the neighbour nodes. + * 6) update the set's size. + */ + +t_error set_after_ll(i_set setid, + s_iterator iterator, + void* data) +{ + s_set_ll_node* i = iterator.u.ll.node; + s_set_ll_node* n; + o_set* o; + + /* + * 0) + */ + + if (data == NULL) + CORE_ESCAPE("the 'data' argument is null"); + + if (*((t_id*)data) == ID_UNUSED) + CORE_ESCAPE("the provided object must begin with a valid identifier"); + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + if (o->options & SET_OPTION_SORT) + CORE_ESCAPE("unable to insert at a precise location in a sorted set"); + + /* + * 3) + */ + + if ((n = malloc(sizeof (s_set_ll_node))) == NULL) + CORE_ESCAPE("unable to allocate memory for the linked-list node"); + + memset(n, 0x0, sizeof (s_set_ll_node)); + + /* + * 4) + */ + + if (o->options & SET_OPTION_ALLOCATE) + { + if ((n->data = malloc(o->datasz)) == NULL) + { + free(n); + + CORE_ESCAPE("unable to allocate memory for the object's copy"); + } + + memcpy(n->data, data, o->datasz); + } + else + { + n->data = data; + } + + /* + * 5) + */ + + n->next = i->next; + n->previous = i; + + i->next = n; + + if (n->next != NULL) + n->next->previous = n; + else + o->u.ll.tail = n; + + /* + * 6) + */ + + o->size++; + + CORE_LEAVE(); +} + +/* + * this function adds an object in the linked-list. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) allocate and initialize the new node. + * 3) clone the object if required. + * 4) insert the new node and arrange the neighbour nodes depending on + * the options. + * A) if the SORT option has been activated... + * a) insert the new node in the list. + * i) there is an identifier collision; the operation is cancelled. + * ii) try to insert the new node in the list i.e before a node + * with a higher identifier. + * b) if no such node has been found, insert the node at the end + * of the list. + * c) if the list is empty: the new node becomes the list. + * B) otherwise... + * a) insert the node at the head of the list. + * 5) update the set's size. + */ + +t_error set_add_ll(i_set setid, + void* data) +{ + s_set_ll_node* n; + o_set* o; + + /* + * 0) + */ + + if (data == NULL) + CORE_ESCAPE("the 'data' argument is null"); + + if (*((t_id*)data) == ID_UNUSED) + CORE_ESCAPE("the provided object must begin with a valid identifier"); + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + if ((n = malloc(sizeof (s_set_ll_node))) == NULL) + CORE_ESCAPE("unable to allocate memory for the linked-list node"); + + memset(n, 0x0, sizeof (s_set_ll_node)); + + /* + * 3) + */ + + if (o->options & SET_OPTION_ALLOCATE) + { + if ((n->data = malloc(o->datasz)) == NULL) + { + free(n); + + CORE_ESCAPE("unable to allocate memory for the object's copy"); + } + + memcpy(n->data, data, o->datasz); + } + else + { + n->data = data; + } + + /* + * 4) + */ + + if (o->options & SET_OPTION_SORT) + { + /* + * A) + */ + + if (o->u.ll.head != NULL) + { + s_set_ll_node* tmp; + + /* + * a) + */ + + for (tmp = o->u.ll.head; tmp != NULL; tmp = tmp->next) + { + if (*((t_id*)n->data) == *((t_id*)tmp->data)) + { + /* + * i) + */ + + if ((o->options & SET_OPTION_ALLOCATE) || + (o->options & SET_OPTION_FREE)) + free(n->data); + + free(n); + + CORE_ESCAPE("identifier collision detected in the set " + "%qd on the object identifier %qd", + o->id, + *((t_id*)n->data)); + } + + if (*((t_id*)n->data) < *((t_id*)tmp->data)) + { + /* + * ii) + */ + + n->previous = tmp->previous; + n->next = tmp; + + if (n->previous != NULL) + n->previous->next = n; + else + o->u.ll.head = n; + + if (n->next != NULL) + n->next->previous = n; + else + o->u.ll.tail = n; + + break; + } + } + + /* + * b + */ + + if (tmp == NULL) + { + n->previous = o->u.ll.tail; + n->next = NULL; + + if (n->previous != NULL) + n->previous->next = n; + else + o->u.ll.head = n; + + o->u.ll.tail = n; + } + } + else + { + /* + * c) + */ + + n->previous = NULL; + n->next = NULL; + + o->u.ll.head = n; + o->u.ll.tail = n; + } + } + else + { + /* + * B) + */ + + /* + * a) + */ + + n->previous = NULL; + n->next = o->u.ll.head; + + if (n->next != NULL) + n->next->previous = n; + + o->u.ll.head = n; + + if (o->u.ll.tail == NULL) + o->u.ll.tail = n; + } + + /* + * 5) + */ + + o->size++; + + CORE_LEAVE(); +} + +/* + * this function removes a node from the list. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) locate the object to remove. + * 3) save a pointer on the node. + * 4) arrange the list so that the node no longer gets referenced. + * 5) release the object's memory, if required. + * 6) release the node's memory. + * 7) update the set's size. + */ + +t_error set_remove_ll(i_set setid, + t_id id) +{ + s_set_ll_node* tmp; + o_set* o; + s_iterator i; + + /* + * 0) + */ + + if (id == ID_UNUSED) + CORE_ESCAPE("invalid identifier"); + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + if (set_locate(setid, id, &i) != ERROR_OK) + CORE_ESCAPE("unable to locate the object in the set"); + + /* + * 3) + */ + + tmp = i.u.ll.node; + + /* + * 4) + */ + + if (tmp->previous != NULL) + tmp->previous->next = tmp->next; + else + o->u.ll.head = tmp->next; + + if (tmp->next != NULL) + tmp->next->previous = tmp->previous; + else + o->u.ll.tail = tmp->previous; + + /* + * 5) + */ + + if ((o->options & SET_OPTION_ALLOCATE) || + (o->options & SET_OPTION_FREE)) + free(tmp->data); + + /* + * 6) + */ + + free(tmp); + + /* + * 7) + */ + + o->size--; + + CORE_LEAVE(); +} + +/* + * this function deletes an element whose location is given by the iterator. + * + * steps: + * + * 1) retrieve the set descriptor. + * 2) save the node's address. + * 3) if the iterator is invalid, return an error. + * 4) arrange the list so that the node no longer gets referenced. + * 5) release the object's memory, if required. + * 6) release the node's memory. + * 7) update the set's size. + */ + +t_error set_delete_ll(i_set setid, + s_iterator iterator) +{ + s_set_ll_node* n; + o_set* o; + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + n = iterator.u.ll.node; + + /* + * 3) + */ + + if (n == NULL) + CORE_ESCAPE("the iterator's internal pointer is invalid"); + + /* + * 4) + */ + + if (n->previous) + n->previous->next = n->next; + else + o->u.ll.head = n->next; + + if (n->next) + n->next->previous = n->previous; + else + o->u.ll.tail = n->previous; + + /* + * 5) + */ + + if (o->options & SET_OPTION_FREE || + o->options & SET_OPTION_ALLOCATE) + free(n->data); + + /* + * 6) + */ + + free(n); + + /* + * 7) + */ + + o->size--; + + CORE_LEAVE(); +} + +/* + * this function flushes the set. + * + * steps + * + * 1) retrieve the set descriptor. + * 2) go through the objects. + * a) if required, release the object's memory. + * b) release the node's memory. + * 3) re-initialize the head and tail pointers. + * 4) re-set the size to zero. + */ + +t_error set_flush_ll(i_set setid) +{ + s_set_ll_node* tmp; + o_set* o; + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + for (tmp = o->u.ll.tail; tmp != NULL; ) + { + s_set_ll_node* t = tmp->previous; + + if ((o->options & SET_OPTION_ALLOCATE) || + (o->options & SET_OPTION_FREE)) + free(tmp->data); + + free(tmp); + + tmp = t; + } + + /* + * 3) + */ + + o->u.ll.head = NULL; + o->u.ll.tail = NULL; + + /* + * 4) + */ + + o->size = 0; + + CORE_LEAVE(); +} + +/* + * this function tries to locate an object according to its identifier + * and build a corresponding iterator. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) go through the objects and return an iterator if found. + * 3) otherwise, return an error. + */ + +t_error set_locate_ll(i_set setid, + t_id id, + s_iterator* iterator) +{ + s_set_ll_node* tmp; + o_set* o; + + /* + * 0) + */ + + if (iterator == NULL) + CORE_ESCAPE("the 'iterator' argument is null"); + + if (id == ID_UNUSED) + CORE_ESCAPE("invalid object identifier"); + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + for (tmp = o->u.ll.head; tmp != NULL; tmp = tmp->next) + { + if (*((t_id*)tmp->data) == id) + { + iterator->u.ll.node = tmp; + + CORE_LEAVE(); + } + } + + /* + * 3) + */ + + CORE_ESCAPE("unable to find the given object identifier in the set"); +} + +/* + * this function returns an object given its iterator. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) return the object. + */ + +t_error set_object_ll(i_set setid, + s_iterator iterator, + void** data) +{ + s_set_ll_node* n = iterator.u.ll.node; + o_set* o; + + /* + * 0) + */ + + if (data == NULL) + CORE_ESCAPE("the 'data' argument is null"); + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + *data = n->data; + + CORE_LEAVE(); +} + +/* + * this function reserves a set according to several options. + * + * steps: + * + * 0) verify the arguments. + * 1) assign an identifier, depending on the options: either this is + * the set container or not. if it is, take the set container identifier + * that is in the set manager. + * 2) initialize and fill the set descriptor. + * 3) register the set descriptor. + */ + +t_error set_reserve_ll(t_options options, + t_size datasz, + i_set* id) +{ + o_set o; + + /* + * 0) + */ + + if (datasz < sizeof (t_id)) + CORE_ESCAPE("unable to reserve a set for objects smaller than " + "an identifier"); + + if (id == NULL) + CORE_ESCAPE("the 'id' argument is null"); + + if (options & SET_OPTION_ORGANISE) + CORE_ESCAPE("unable to set the organise option since linked-list sets " + "are implicitly organised"); + + if ((options & SET_OPTION_ALLOCATE) && (options & SET_OPTION_FREE)) + CORE_ESCAPE("unable to reserve a set with both alloc and free options"); + + /* + * 1) + */ + + if (options & SET_OPTION_CONTAINER) + { + *id = _set.sets; + } + else + { + if (id_reserve(&_set.id, id) != ERROR_OK) + CORE_ESCAPE("unable to reserve the set identifier"); + } + + /* + * 2) + */ + + memset(&o, 0x0, sizeof (o_set)); + + o.id = *id; + o.size = 0; + o.type = SET_TYPE_LL; + o.options = options; + o.datasz = datasz; + + o.u.ll.head = NULL; + o.u.ll.tail = NULL; + + /* + * 3) + */ + + if (set_new(&o) != ERROR_OK) + { + if (!(options & SET_OPTION_CONTAINER)) + id_release(&_set.id, o.id); + + CORE_ESCAPE("unable to register the set descriptor"); + } + + CORE_LEAVE(); +} + +/* + * this function releases a set. + * + * steps: + * + * 1) retrieve the set descriptor. + * 2) flush the set. + * 3) release the set identifier. + * 4) remove the set descriptor from the set container. + */ + +t_error set_release_ll(i_set setid) +{ + o_set *o; + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + if (set_flush(setid) != ERROR_OK) + CORE_ESCAPE("unable to flush the set"); + + /* + * 3) + */ + + if (id_release(&_set.id, o->id) != ERROR_OK) + CORE_ESCAPE("unable to release the set identifier"); + + /* + * 4) + */ + + if (set_destroy(o->id) != ERROR_OK) + CORE_ESCAPE("unable to destroy the set descriptor"); + + CORE_LEAVE(); +} + +/* + * this function just returns an error because the ll set does not + * support this operation. + */ + +t_error set_push_ll(i_set setid, + void* data) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} + +/* + * this function just returns an error because the ll set does not + * support this operation. + */ + +t_error set_pop_ll(i_set setid) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} + +/* + * this function just returns an error because the ll set does not + * support this operation. + */ + +t_error set_pick_ll(i_set setid, + void** data) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} diff --git a/kaneton/core/set/set-pipe.c b/kaneton/core/set/set-pipe.c new file mode 100644 index 0000000..9289701 --- /dev/null +++ b/kaneton/core/set/set-pipe.c @@ -0,0 +1,391 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/set/set-pipe.c + * + * created renaud voltz [wed jan 25 17:11:05 2006] + * updated julien quintard [sun jan 30 20:35:16 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this set implementation provides a FIFO data structure. + * + * note that the implementation actually relies on the linked-list + * implementation. + * + * options: + * SET_OPTION_ALLOCATE + * SET_OPTION_FREE + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the set manager's structure. + */ + +extern m_set _set; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function reserves a set according to the given options. + * + * steps: + * + * 0) verify the arguments. + * 1) reserves an identifier. + * 2) initialize and fill the set descriptor. + * 3) register the set descriptor. + */ + +t_error set_reserve_pipe(t_options options, + t_size datasz, + i_set* id) +{ + o_set o; + + /* + * 0) + */ + + if (datasz < sizeof (t_id)) + CORE_ESCAPE("unable to reserve a set for objects smaller than " + "an identifier"); + + if (id == NULL) + CORE_ESCAPE("the 'id' argument is null"); + + if (options & SET_OPTION_CONTAINER) + CORE_ESCAPE("this type of set cannot be used as a container"); + + if (options & SET_OPTION_ORGANISE) + CORE_ESCAPE("this type of set does not support the organise option"); + + if ((options & SET_OPTION_ALLOCATE) && (options & SET_OPTION_FREE)) + CORE_ESCAPE("unable to reserve a set with both alloc and free options"); + + /* + * 1) + */ + + if (id_reserve(&_set.id, id) != ERROR_OK) + CORE_ESCAPE("unable to reserve the set identifier"); + + /* + * 2) + */ + + memset(&o, 0x0, sizeof (o_set)); + + o.id = *id; + o.size = 0; + o.type = SET_TYPE_PIPE; + o.options = options; + o.datasz = datasz; + + o.u.ll.head = NULL; + o.u.ll.tail = NULL; + + /* + * 3) + */ + + if (set_new(&o) != ERROR_OK) + CORE_ESCAPE("unable to register the set descriptor"); + + CORE_LEAVE(); +} + +/* + * this function just returns an error. + */ + +t_error set_exist_pipe(i_set setid, + t_id id) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} + +/* + * this function shows the set's attributes. + */ + +t_error set_show_pipe(i_set setid, + mt_margin margin) +{ + if (set_show_ll(setid, margin) != ERROR_OK) + CORE_ESCAPE("unable to show the linked-list set"); + + CORE_LEAVE(); +} + +/* + * this function releases a set. + */ + +t_error set_release_pipe(i_set setid) +{ + if (set_release_ll(setid) != ERROR_OK) + CORE_ESCAPE("unable to release the linked-list set"); + + CORE_LEAVE(); +} + +/* + * this function flushes the set. + */ + +t_error set_flush_pipe(i_set setid) +{ + if (set_flush_ll(setid) != ERROR_OK) + CORE_ESCAPE("unable to flush the linked-list set"); + + CORE_LEAVE(); +} + +/* + * this function adds an object to the pipe. + */ + +t_error set_push_pipe(i_set setid, + void* data) +{ + if (set_insert_ll(setid, data) != ERROR_OK) + CORE_ESCAPE("unable to insert in the linked-list set"); + + CORE_LEAVE(); +} + + +/* + * this function returns the about-to-get-out object from the pipe. + * + * steps: + * + * 1) locate the last object. + * 2) retrieve it. + */ + +t_error set_pick_pipe(i_set setid, + void** data) +{ + s_iterator iterator; + + /* + * 1) + */ + + if (set_tail_ll(setid, &iterator) != ERROR_TRUE) + CORE_ESCAPE("unable to locate the tail object in the linked-list set"); + + /* + * 2) + */ + + if (set_object_ll(setid, iterator, data) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the object"); + + CORE_LEAVE(); +} + +/* + * this function deletes the about-to-get-out object. + * + * steps: + * + * 1) locate the last object. + * 2) delete it. + */ + +t_error set_pop_pipe(i_set setid) +{ + s_iterator iterator; + + /* + * 1) + */ + + if (set_tail_ll(setid, &iterator) != ERROR_TRUE) + CORE_ESCAPE("unable to locate the tail object in the linked-list set"); + + /* + * 2) + */ + + if (set_delete_ll(setid, iterator) != ERROR_OK) + CORE_ESCAPE("unable to delete the object"); + + CORE_LEAVE(); +} + +/* + * this function returns an iterator on the first element of the pipe + */ + +t_error set_head_pipe(i_set setid, + s_iterator* iterator) +{ + if (set_head_ll(setid, iterator) != ERROR_TRUE) + CORE_FALSE(); + + CORE_TRUE(); +} + + +/* + * this function returns an iterator on the last element of the pipe. + */ + +t_error set_tail_pipe(i_set setid, + s_iterator* iterator) +{ + if (set_tail_ll(setid, iterator) != ERROR_TRUE) + CORE_FALSE(); + + CORE_TRUE(); +} + +/* + * this function returns an iterator on the previous node. + */ + +t_error set_previous_pipe(i_set setid, + s_iterator current, + s_iterator* previous) +{ + if (set_previous_ll(setid, current, previous) != ERROR_TRUE) + CORE_FALSE(); + + CORE_TRUE(); +} + +/* + * this function returns an iterator on the next node. + */ + +t_error set_next_pipe(i_set setid, + s_iterator current, + s_iterator* next) +{ + if (set_next_ll(setid, current, next) != ERROR_TRUE) + CORE_FALSE(); + + CORE_TRUE(); +} + +/* + * this function just returns an error. + */ + +t_error set_insert_pipe(i_set setid, + void* data) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} + +/* + * this function just returns an error. + */ + +t_error set_append_pipe(i_set setid, + void* data) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} + +/* + * this function just returns an error. + */ + +t_error set_before_pipe(i_set setid, + s_iterator iterator, + void* data) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} + +/* + * this function just returns an error. + */ + +t_error set_after_pipe(i_set setid, + s_iterator iterator, + void* data) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} + +/* + * this function just returns an error. + */ + +t_error set_add_pipe(i_set setid, + void* data) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} + +/* + * this function just returns an error. + */ + +t_error set_remove_pipe(i_set setid, + t_id id) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} + +/* + * this function just returns an error. + */ + +t_error set_delete_pipe(i_set setid, + s_iterator iterator) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} + +/* + * this function just returns an error. + */ + +t_error set_locate_pipe(i_set setid, + t_id id, + s_iterator* iterator) +{ + if (set_locate_ll(setid, id, iterator) != ERROR_OK) + CORE_ESCAPE("unable to locate the object from the linked-list set"); + + CORE_LEAVE(); +} + +/* + * this function returns the object on which the given iterator points to. + */ + +t_error set_object_pipe(i_set setid, + s_iterator iterator, + void** data) +{ + if (set_object_ll(setid, iterator, data) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the object from the linked-list set"); + + CORE_LEAVE(); +} diff --git a/kaneton/core/set/set-stack.c b/kaneton/core/set/set-stack.c new file mode 100644 index 0000000..3d3ee68 --- /dev/null +++ b/kaneton/core/set/set-stack.c @@ -0,0 +1,386 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/set/set-stack.c + * + * created renaud voltz [wed jan 25 17:11:05 2006] + * updated julien quintard [sun jan 30 20:35:23 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this set implementation provides a LIFO data structure. + * + * note that the implementation actually relies on the linked-list + * implementation. + * + * options: + * SET_OPTION_ALLOCATE + * SET_OPTION_FREE + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- externs --------------------------------------------------------- + */ + +extern m_set _set; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function reserves a set according to the given options. + * + * steps: + * + * 0) verify the arguments. + * 1) reserves an identifier. + * 2) initialize and fill the set descriptor. + * 3) register the set descriptor. + */ + +t_error set_reserve_stack(t_options options, + t_size datasz, + i_set* id) +{ + o_set o; + + /* + * 0) + */ + + if (datasz < sizeof (t_id)) + CORE_ESCAPE("unable to reserve a set for objects smaller than " + "an identifier"); + + if (id == NULL) + CORE_ESCAPE("the 'id' argument is null"); + + if (options & SET_OPTION_CONTAINER) + CORE_ESCAPE("this type of set cannot be used as a container"); + + if (options & SET_OPTION_ORGANISE) + CORE_ESCAPE("this type of set does not support the organise option"); + + if ((options & SET_OPTION_ALLOCATE) && (options & SET_OPTION_FREE)) + CORE_ESCAPE("unable to reserve a set with both alloc and free options"); + + /* + * 1) + */ + + if (id_reserve(&_set.id, id) != ERROR_OK) + CORE_ESCAPE("unable to reserve the set identifier"); + + /* + * 2) + */ + + memset(&o, 0x0, sizeof (o_set)); + + o.id = *id; + o.size = 0; + o.type = SET_TYPE_STACK; + o.options = options; + o.datasz = datasz; + + o.u.ll.head = NULL; + o.u.ll.tail = NULL; + + /* + * 3) + */ + + if (set_new(&o) != ERROR_OK) + CORE_ESCAPE("unable to register the set descriptor"); + + CORE_LEAVE(); +} + +/* + * this function just returns an error. + */ + +t_error set_exist_stack(i_set setid, + t_id id) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} + +/* + * this function shows the set's attributes. + */ + +t_error set_show_stack(i_set setid, + mt_margin margin) +{ + if (set_show_ll(setid, margin) != ERROR_OK) + CORE_ESCAPE("unable to show the linked-list set"); + + CORE_LEAVE(); +} + +/* + * this function releases the set. + */ + +t_error set_release_stack(i_set setid) +{ + if (set_release_ll(setid) != ERROR_OK) + CORE_ESCAPE("unable to release the linked-list set"); + + CORE_LEAVE(); +} + +/* + * this function flushes the set. + */ + +t_error set_flush_stack(i_set setid) +{ + if (set_flush_ll(setid) != ERROR_OK) + CORE_ESCAPE("unable to flush the linked-list set"); + + CORE_LEAVE(); +} + +/* + * this function adds an object to the stack. + */ + +t_error set_push_stack(i_set setid, + void* data) +{ + if (set_insert_ll(setid, data) != ERROR_OK) + CORE_ESCAPE("unable to insert in the linked-list set"); + + CORE_LEAVE(); +} + + +/* + * this function returns the object on top of the stack. + * + * steps: + * + * 1) locate the head object. + * 2) retrieve it. + */ + +t_error set_pick_stack(i_set setid, + void** data) +{ + s_iterator iterator; + + /* + * 1) + */ + + if (set_head_ll(setid, &iterator) != ERROR_TRUE) + CORE_ESCAPE("unable to locate the head of the linked-list set"); + + /* + * 2) + */ + + if (set_object_ll(setid, iterator, data) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the object"); + + CORE_LEAVE(); +} + +/* + * this function deletes the object on top of the stack. + * + * steps: + * + * 1) locate the head object. + * 2) delete it. + */ + +t_error set_pop_stack(i_set setid) +{ + s_iterator iterator; + + /* + * 1) + */ + + if (set_head_ll(setid, &iterator) != ERROR_TRUE) + CORE_ESCAPE("unable to locate the head of the linked-list set"); + + /* + * 2) + */ + + if (set_delete_ll(setid, iterator) != ERROR_OK) + CORE_ESCAPE("unable to delete the object"); + + CORE_LEAVE(); +} + +/* + * this function returns an iterator on the first node of the stack + */ + +t_error set_head_stack(i_set setid, + s_iterator* iterator) +{ + if (set_head_ll(setid, iterator) != ERROR_TRUE) + CORE_FALSE(); + + CORE_TRUE(); +} + + +/* + * this function returns an iterator on the last node of the stack. + */ +t_error set_tail_stack(i_set setid, + s_iterator* iterator) +{ + if (set_tail_ll(setid, iterator) != ERROR_TRUE) + CORE_FALSE(); + + CORE_TRUE(); +} + +/* + * this function returns an iterator on the previous node. + */ + +t_error set_previous_stack(i_set setid, + s_iterator current, + s_iterator* previous) +{ + if (set_previous_ll(setid, current, previous) != ERROR_TRUE) + CORE_FALSE(); + + CORE_TRUE(); +} + +/* + * this function returns an iterator on the next node. + */ + +t_error set_next_stack(i_set setid, + s_iterator current, + s_iterator* next) +{ + if (set_next_ll(setid, current, next) != ERROR_TRUE) + CORE_FALSE(); + + CORE_TRUE(); +} + +/* + * this function just returns an error. + */ + +t_error set_insert_stack(i_set setid, + void* data) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} + +/* + * this function just returns an error. + */ + +t_error set_append_stack(i_set setid, + void* data) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} + +/* + * this function just returns an error. + */ + +t_error set_before_stack(i_set setid, + s_iterator iterator, + void* data) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} + +/* + * this function just returns an error. + */ + +t_error set_after_stack(i_set setid, + s_iterator iterator, + void* data) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} + +/* + * this function just returns an error. + */ + +t_error set_add_stack(i_set setid, + void* data) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} + +/* + * this function just returns an error. + */ + +t_error set_remove_stack(i_set setid, + t_id id) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} + +/* + * this function just returns an error. + */ + +t_error set_delete_stack(i_set setid, + s_iterator iterator) +{ + CORE_ESCAPE("this type of set does not support this operation"); +} + +/* + * this function just returns an error. + */ + +t_error set_locate_stack(i_set setid, + t_id id, + s_iterator* iterator) +{ + if (set_locate_ll(setid, id, iterator) != ERROR_OK) + CORE_ESCAPE("unable to locate the object from the linked-list set"); + + CORE_LEAVE(); +} + +/* + * this function returns the object the given iterator points to. + */ + +t_error set_object_stack(i_set setid, + s_iterator iterator, + void** data) +{ + if (set_object_ll(setid, iterator, data) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the object from the linked-list set"); + + CORE_LEAVE(); +} diff --git a/kaneton/core/set/set.c b/kaneton/core/set/set.c new file mode 100644 index 0000000..21f596d --- /dev/null +++ b/kaneton/core/set/set.c @@ -0,0 +1,592 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/set/set.c + * + * created julien quintard [fri dec 2 19:55:19 2005] + * updated julien quintard [sun jan 30 20:34:47 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * the set manager is used to manage the various kernel's data structures. + * indeed, every kernel manager including the task manager, the thread manager, + * the segment manager etc. uses the set manager to store its objects rather + * than creating and managing data structures on their own. + * + * in order to add a set implementation, referred to as XYZ, to the set + * manager, one must complete the following steps: + * + * 1) create the kaneton/core/set/set_XYZ.c file and modify the Makefile + * to take this new file in account; + * 2) create the kaneton/include/core/set_XYZ.h file and modify the + * set.h to include this new file; + * 3) add the new set implementation to the union contained in the + * set object: o_set, in the set.h header file; + * 4) add the new set implementation type in the switch case contained + * in the set_trap() macro, in the set.h header file. + * + * the set manager relies on macro-functions to provide the set implementations + * with a different number of arguments each with its own type. + * + * each set implementation provides some options which may vary depending + * on the nature of the underlying data structure. + * + * the most common options are: + * o the ALLOCATE option indicates that the objects to be added must be + * cloned i.e the set implementation must allocate its own copy of + * the object before insertion. + * o the SORT option requests the set implementation to sort the objects + * to be stored. note that this option may render the implementation + * extremely inefficient. besides, note that the identifier collision + * detection can only operate with this option. + * o beware of inserting pre-allocated objects. indeed, considering the + * set_flush() operation, the set manager would clear the data structure + * without releasing the object as it does not know they have been + * pre-allocated with malloc(). to counter this issue, the FREE option + * has been introduced, instructing the set manager to release the + * memory of the object when released. + * o the option CONTAINER is only used internally to build the very + * first set which is the set container i.e the set of sets. + * o the ORGANISE option specifies the set implementation to always try + * to re-organise the objects. this option is used, for example, by + * the 'array' implementation which shifts the objects whenever + * an element is removed . + * + * note that the set manager relies on the malloc() and free() functions + * when it comes to memory management, including related to the ALLOCATE + * and FREE options. + * + * every set can store objects which comply to both (i) the definition of + * an object and (ii) the set implementation's configuration. first, an + * object is a data element which begins with a kaneton identifier (t_id). + * this identifier is used to identify the object in the set, hence locate + * them for deletion, retrieval, modification etc. second, whenever a set + * is reserved, the size of the objects to be stored is provided so that + * memory can be optimised. + * + * the programmer must take notice of the documentation related to a set + * implementation before using it. however, most set implementation comply + * with a generic interface. the following detail some of the most fondamental + * set operations: + * + * the set_reserve(type, options, datasz, &id) function reserves a set + * of the type---i.e set implementation---'type' with some options. the + * 'datasz' argument indicates the size of the objects about the be stored + * while the 'id' argument is the output set identifier. note that extra + * arguments may be required depending on the set implementation. + * + * the set_release(id) function releases the set identified by 'id'. + * + * the set_object(id, iterator, &object) function takes a set identifier + * along with an iterator pointing to an element of the set and returns + * the address of the iterator's object in 'object'. + * + * in addition, objects can be added/removed/pushed/poped/inserted etc. + * depending on the set implementation. for example, the set_add(id, object) + * add the 'object' to the set 'id'. + * + * note that this manager is considered fondamental as every other manager + * relies on it. this manager therefore does no relies on the machine, + * not mentioning that this functionality provided, i.e data structure + * management, has nothing to do with the underlying machine. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the set manager. + */ + +m_set _set; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function dumps the set manager. + * + * steps: + * + * 1) retrieve the set container's descriptor. + * 2) display general information. + * 3) show the identifier object. + * 4) go through the sets. + * a) retrieve the set descriptor. + * b) show the set. + */ + +t_error set_dump(void) +{ + t_state state; + o_set* data; + o_set* o; + s_iterator i; + + /* + * 1) + */ + + if (set_descriptor(_set.sets, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + module_call(console, message, + '#', "set manager: sets(%qd)\n", + _set.sets); + + /* + * 3) + */ + + if (id_show(&_set.id, MODULE_CONSOLE_MARGIN_SHIFT) != ERROR_OK) + CORE_ESCAPE("unable to show the identifier object"); + + /* + * 4) + */ + + module_call(console, message, + '#', " sets: id(%qd) size(%qd)\n", + o->id, + o->size); + + set_foreach(SET_OPTION_FORWARD, o->id, &i, state) + { + /* + * a) + */ + + if (set_object(o->id, i, (void**)&data) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set object corresponding " + "to its identifier"); + + /* + * b) + */ + + if (set_show(data->id, + 2 * MODULE_CONSOLE_MARGIN_SHIFT) != ERROR_OK) + CORE_ESCAPE("unable to show the set"); + } + + CORE_LEAVE(); +} + +/* + * this function returns true if the set is empty. + * + * steps: + * + * 1) retrieve the set descriptor. + * 2) return false if there is at least an object in the set, true otherwise. + */ + +t_error set_empty(i_set setid) +{ + o_set* o; + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + if (o->size > 0) + CORE_FALSE(); + + CORE_TRUE(); +} + +/* + * this function returns the size of the set. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the set descriptor. + * 2) set the size to return. + */ + +t_error set_size(i_set setid, + t_setsz* size) +{ + o_set* o; + + /* + * 0) + */ + + if (size == NULL) + CORE_ESCAPE("the 'size' argument is null"); + + /* + * 1) + */ + + if (set_descriptor(setid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the set descriptor"); + + /* + * 2) + */ + + *size = o->size; + + CORE_LEAVE(); +} + +/* + * this function adds a set descriptor to the set container. + * + * steps: + * + * 0) verify the arguments. + * 1) add the object depending on its identifier. + * A) if the object is the very first one, it is considered as being the + * container... + * a) allocate memory for the set container's object because the received + * object has not been dynamically allocated. indeed the set container + * is configured to clone its object i.e the set objects. therefore + * the reserved object must be cloned as well. + * b) copy the object's content. + * B) if not... + * a) add the object into the set container. + */ + +t_error set_new(o_set* object) +{ + /* + * 0) + */ + + if (object == NULL) + CORE_ESCAPE("the 'object' argument is null"); + + /* + * 1) + */ + + if (object->id == _set.sets) + { + /* + * A) + */ + + /* + * a) + */ + + if ((_set.container = malloc(sizeof (o_set))) == NULL) + CORE_ESCAPE("unable to allocate memory for the set container"); + + /* + * b) + */ + + memcpy(_set.container, object, sizeof (o_set)); + } + else + { + /* + * B) + */ + + /* + * a) + */ + + if (set_add(_set.sets, object) != ERROR_OK) + CORE_ESCAPE("unable to add the set descriptor to the set container"); + } + + CORE_LEAVE(); +} + +/* + * this function removes a set descriptor from the set container. + * + * steps: + * + * 1) removes the set depending on its identifier. + * A) the set to remove is the set container... + * a) release the container's memory. + * B) the set is a normal object... + * a) remove it from the set container. + */ + +t_error set_destroy(i_set setid) +{ + /* + * 1) + */ + + if (setid == _set.sets) + { + /* + * A) + */ + + /* + * a) + */ + + free(_set.container); + } + else + { + /* + * B) + */ + + /* + * a) + */ + + if (set_remove(_set.sets, setid) != ERROR_OK) + CORE_ESCAPE("unable to remove the descriptor from the set container"); + } + + CORE_LEAVE(); +} + +/* + * this function returns the set descriptor, i.e set object, corresponding + * to a set identifier. + * + * steps: + * + * 0) verify the arguments. + * 1) locate the descriptor depending on the set identifier. + * A) if the set is the container... + * a) return the address of the set container which is located in + * the manager's structure. + * B) otherwise... + * a) retrieve the set object from the set container. + */ + +t_error set_descriptor(i_set setid, + o_set** object) +{ + /* + * 0) + */ + + if (object == NULL) + CORE_ESCAPE("the 'object' argument is null"); + + if (setid == ID_UNUSED) + CORE_ESCAPE("invalid set identifier"); + + /* + * 1) + */ + + if (setid == _set.sets) + { + /* + * A) + */ + + /* + * a) + */ + + *object = _set.container; + } + else + { + /* + * B) + */ + + /* + * a) + */ + + if (set_get(_set.sets, setid, (void**)object) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the descriptor object from " + "the set container"); + } + + CORE_LEAVE(); +} + +/* + * this function returns an object given its identifier. note that this + * function is useful in many cases because the programmer does not + * have to perform a manual walk through the set objects. + * + * steps: + * + * 0) verify the arguments. + * 1) locate the object in the set according to its identifier. + * 2) retrieve the object from its iterator. + */ + +t_error set_get(i_set setid, + t_id id, + void** object) +{ + s_iterator iterator; + + /* + * 0) + */ + + if (object == NULL) + CORE_ESCAPE("the 'object' argument is null"); + + /* + * 1) + */ + + if (set_locate(setid, id, &iterator) != ERROR_OK) + CORE_ESCAPE("unable to locate the object in the set"); + + /* + * 2) + */ + + if (set_object(setid, iterator, object) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the object"); + + CORE_LEAVE(); +} + +/* + * this function initializes the set manager. + * + * be careful, the container have to be build using the options + * SET_OPTION_CONTAINER and SET_OPTION_ALLOCATE. + * + * steps: + * + * 1) display a message. + * 2) initialize the set manager's structure. + * 3) build the identifier object used to generate set identifiers. + * 4) reserve an identifier for the set container. + * 5) reserve the set container which will hold the set descriptors i.e + * set objects. + */ + +t_error set_initialize(void) +{ + i_set needless; + + /* + * 1) + */ + + module_call(console, message, + '+', "initializing the set manager\n"); + + /* + * 2) + */ + + memset(&_set, 0x0, sizeof (m_set)); + + /* + * 3) + */ + + if (id_build(&_set.id) != ERROR_OK) + CORE_ESCAPE("unable to build the identifier object"); + + /* + * 4) + */ + + if (id_reserve(&_set.id, &_set.sets) != ERROR_OK) + CORE_ESCAPE("unable to reserve the identifier for the set container"); + + /* + * 5) + */ + + if (set_reserve(bpt, + SET_OPTION_CONTAINER | SET_OPTION_ALLOCATE | SET_OPTION_SORT, + sizeof (o_set), + ___kaneton$pagesz, + &needless) != ERROR_OK) + CORE_ESCAPE("unable to reserve the set container"); + + CORE_LEAVE(); +} + +/* + * this function cleans the set manager. + * + * steps: + * + * 1) display a message. + * 2) release every set object the set container contains. + * 3) release the set container. + * 4) destroys the identifier object. + */ + +t_error set_clean(void) +{ + s_iterator iterator; + + /* + * 1) + */ + + module_call(console, message, + '+', "cleaning the set manager\n"); + + /* + * 2) + */ + + while (set_head(_set.sets, &iterator) == ERROR_TRUE) + { + o_set* o; + + if (set_object(_set.sets, iterator, (void**)&o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the object from the set"); + + if (set_release(o->id) != ERROR_OK) + CORE_ESCAPE("unable to releases the set object"); + } + + /* + * 3) + */ + + if (set_release(_set.sets) != ERROR_OK) + CORE_ESCAPE("unable to release the set container"); + + /* + * 4) + */ + + if (id_destroy(&_set.id) != ERROR_OK) + CORE_ESCAPE("unable to destroy the identifier object"); + + CORE_LEAVE(); +} diff --git a/kaneton/core/task/Makefile b/kaneton/core/task/Makefile new file mode 100644 index 0000000..b86f093 --- /dev/null +++ b/kaneton/core/task/Makefile @@ -0,0 +1,76 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/buckman/kaneton/kaneton/core/task/Makefile +# +# created julien quintard [sun jun 10 17:09:05 2007] +# updated matthieu bucchianeri [sat sep 1 12:29:38 2007] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/core/task + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +TASK_C := task.c + +TASK_O := $(TASK_C:.c=.o) + +TASK_INCLUDE := $(_CORE_INCLUDE_DIR_)/task.h + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(_TASK_LO_) + +$(_TASK_LO_): $(TASK_O) + $(call env_remove,$(_TASK_LO_),) + + $(call env_archive,$(_TASK_LO_),$(TASK_O),) + +clear: + $(call env_remove,$(TASK_O),) + + $(call env_remove,$(_TASK_LO_),) + + $(call env_purge,) + +prototypes: + $(call env_prototypes,$(TASK_INCLUDE),) + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(TASK_C),) + +dependencies: + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/core/task/task.c b/kaneton/core/task/task.c new file mode 100644 index 0000000..fcf673a --- /dev/null +++ b/kaneton/core/task/task.c @@ -0,0 +1,2410 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/task/task.c + * + * created julien quintard [fri jun 22 02:25:26 2007] + * updated julien quintard [thu apr 7 20:59:38 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * the task manager provides functionalities for reserving, releasing, + * waiting for etc. tasks. + * + * a task is a passive entity in the way that it is not actually scheduled. + * however, a task contains several threads which can be scheduled and + * an address space which describes the task's and its threads' useable + * memory. + * + * every task is referenced by a unique task identifier. besides, every + * task is characterised by a class, a behaviour and a priority. + * + * note that every task is assigned to a CPU. this way, the task's threads + * can only be scheduled on this CPU. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * include the machine-specific definitions required by the core. + */ + +machine_include(task); + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the kernel manager. + */ + +extern m_kernel _kernel; + +/* + * the init structure provided by the boot loader. + */ + +extern s_init* _init; + +/* + * the identifier of the pre-reserved segment containing the 'system' + * server's code. + */ + +extern i_segment _system; + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the task manager. + */ + +m_task _task; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function shows a task's attributes. + * + * steps: + * + * 1) retrieve the task object. + * 2) build the state string. + * 3) build the class string. + * 4) build the behaviour string. + * 5) display general information on the task object. + * 6) display the set of children tasks. + * 7) display the set of threads waiting for this task to change state. + * 8) display the tasks's set of threads. + * 9) call the machine. + */ + +t_error task_show(i_task id, + mt_margin margin) +{ + o_task* o; + char* state; + char* class; + char* behaviour; + t_setsz size; + s_iterator i; + t_state s; + + /* + * 1) + */ + + if (task_get(id, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + switch (o->state) + { + case TASK_STATE_START: + { + state = "start"; + + break; + } + case TASK_STATE_STOP: + { + state = "stop"; + + break; + } + case TASK_STATE_BLOCK: + { + state = "block"; + + break; + } + case TASK_STATE_ZOMBIE: + { + state = "zombie"; + + break; + } + case TASK_STATE_DEAD: + { + state = "dead"; + + break; + } + default: + CORE_ESCAPE("unknown task state '%u'", + o->state); + } + + /* + * 3) + */ + + switch (o->class) + { + case TASK_CLASS_KERNEL: + { + class = "kernel"; + + break; + } + case TASK_CLASS_DRIVER: + { + class = "driver"; + + break; + } + case TASK_CLASS_SERVICE: + { + class = "service"; + + break; + } + case TASK_CLASS_GUEST: + { + class = "guest"; + + break; + } + default: + CORE_ESCAPE("unknown task class '%u'", + o->class); + } + + /* + * 4) + */ + + switch (o->behaviour) + { + case TASK_BEHAVIOUR_KERNEL: + { + behaviour = "kernel"; + + break; + } + case TASK_BEHAVIOUR_REALTIME: + { + behaviour = "realtime"; + + break; + } + case TASK_BEHAVIOUR_INTERACTIVE: + { + behaviour = "interactive"; + + break; + } + case TASK_BEHAVIOUR_TIMESHARING: + { + behaviour = "timesharing"; + + break; + } + case TASK_BEHAVIOUR_BACKGROUND: + { + behaviour = "background"; + + break; + } + default: + CORE_ESCAPE("unknown task behaviour '%u'", + o->behaviour); + } + + /* + * 5) + */ + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + "task:\n", + MODULE_CONSOLE_MARGIN_VALUE(margin)); + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " core: id(%qd) cpu(%qd) class(%s) behaviour(%s) priority(%u) " + "state(%s) as(%qd) threads(%qd) parent(%qd) children(%qd) " + "waits(%qd) value(%d) timer(%qd)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + o->id, + o->cpu, + class, + behaviour, + o->priority, + state, + o->as, + o->threads, + o->parent, + o->children, + o->waits, + o->value, + o->timer); + + /* + * 6) + */ + + if (set_size(o->children, &size) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the size of the set of children"); + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " children: id(%qd) size(%qd)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + o->children, + size); + + set_foreach(SET_OPTION_FORWARD, o->children, &i, s) + { + i_task* id; + + if (set_object(o->children, i, (void**)&id) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the task identifier"); + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " task: id(%qd)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + *id); + } + + /* + * 7) + */ + + if (set_size(o->waits, &size) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the size of the set of waiting threads"); + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " waits: id(%qd) size(%qd)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + o->waits, + size); + + set_foreach(SET_OPTION_FORWARD, o->waits, &i, s) + { + i_thread* id; + + if (set_object(o->waits, i, (void**)&id) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread identifier"); + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " thread: id(%qd)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + *id); + } + + /* + * 8) + */ + + if (set_size(o->threads, &size) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the size of the set of threads"); + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " threads: id(%qd) size(%qd)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + o->threads, + size); + + set_foreach(SET_OPTION_FORWARD, o->threads, &i, s) + { + i_thread* id; + + if (set_object(o->threads, i, (void**)&id) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread identifier"); + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " thread: id(%qd)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + *id); + } + + /* + * 9) + */ + + if (machine_call(task, show, id, margin) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function dumps the task manager. + * + * steps: + * + * 1) display general informatin on the manager. + * 2) show the identifier object. + * 3) retrieve the size of the set of tasks. + * 4) go through the manager's tasks and show them. + * 5) retrieve the size of the set of dead tasks. + * 6) go through the set of dead tasks and display their identifiers. + * 7) call the machine. + */ + +t_error task_dump(void) +{ + t_setsz size; + s_iterator i; + t_state s; + + /* + * 1) + */ + + module_call(console, message, + '#', "task manager:\n"); + + module_call(console, message, + '#', " core: tasks(%qd)\n", + _task.tasks); + + /* + * 2) + */ + + if (id_show(&_task.id, + 2 * MODULE_CONSOLE_MARGIN_SHIFT) != ERROR_OK) + CORE_ESCAPE("unable to show the identifier object"); + + /* + * 3) + */ + + if (set_size(_task.tasks, &size) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the size of the set of tasks"); + + /* + * 4) + */ + + module_call(console, message, + '#', " tasks: id(%qd) size(%qd)\n", + _task.tasks, + size); + + set_foreach(SET_OPTION_FORWARD, _task.tasks, &i, s) + { + o_task* o; + + if (set_object(_task.tasks, i, (void**)&o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the task object"); + + if (task_show(o->id, + 3 * MODULE_CONSOLE_MARGIN_SHIFT) != ERROR_OK) + CORE_ESCAPE("unable to show the task"); + } + + /* + * 5) + */ + + if (set_size(_task.morgue.field, &size) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the size of the set of dead tasks"); + + /* + * 6) + */ + + module_call(console, message, + '#', " morgue: field(%qd) timer(%qd)\n", + _task.morgue.field, + _task.morgue.timer); + + module_call(console, message, + '#', " field: id(%qd) size(%qd)\n", + _task.morgue.field, + size); + + set_foreach(SET_OPTION_FORWARD, _task.morgue.field, &i, s) + { + i_task* id; + + if (set_object(_task.tasks, i, (void**)&id) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the task object"); + + module_call(console, message, + '#', " task: id(%qd)\n", + *id); + } + + /* + * 7) + */ + + if (machine_call(task, dump) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function reserves a task object. + * + * steps: + * + * 0) verify the arguments. + * 1) allocate and initialize the object. + * 2) reserve an identifier for the task object. + * 3) fill the parent task field and ask the current task as being of child + * of the parent. note that if the task being reserved is the kernel one, + * ignore this step. + * 4) select the CPU on which the task will be scheduled. + * 5) reserve the set of threads. + * 6) reserve the set of waiting threads. + * 7) reserve the set of children tasks. + * 8) reserve the set of messages. + * 9) add the task to the manager's set of tasks. + * 10) return the identifier of the task. + * 11) call the machine. + */ + +t_error task_reserve(t_class class, + t_behaviour behav, + t_priority prior, + i_task* id) +{ + o_task o; + + /* + * 0) + */ + + if (id == NULL) + CORE_ESCAPE("the 'id' argument is null"); + + if ((class != TASK_CLASS_KERNEL) && + (class != TASK_CLASS_DRIVER) && + (class != TASK_CLASS_SERVICE) && + (class != TASK_CLASS_GUEST)) + CORE_ESCAPE("invalid class"); + + if ((behav != TASK_BEHAVIOUR_KERNEL) && + (behav != TASK_BEHAVIOUR_REALTIME) && + (behav != TASK_BEHAVIOUR_INTERACTIVE) && + (behav != TASK_BEHAVIOUR_TIMESHARING) && + (behav != TASK_BEHAVIOUR_BACKGROUND)) + CORE_ESCAPE("invalid behaviour"); + + switch (behav) + { + case TASK_BEHAVIOUR_KERNEL: + { + if (prior < TASK_PRIORITY_KERNEL_LOW || + prior > TASK_PRIORITY_KERNEL_HIGH) + CORE_ESCAPE("the given priority is inconsistent with the " + "behaviour"); + + break; + } + case TASK_BEHAVIOUR_REALTIME: + { + if (prior < TASK_PRIORITY_REALTIME_LOW || + prior > TASK_PRIORITY_REALTIME_HIGH) + CORE_ESCAPE("the given priority is inconsistent with the " + "behaviour"); + + + break; + } + case TASK_BEHAVIOUR_INTERACTIVE: + { + if (prior < TASK_PRIORITY_INTERACTIVE_LOW || + prior > TASK_PRIORITY_INTERACTIVE_HIGH) + CORE_ESCAPE("the given priority is inconsistent with the " + "behaviour"); + + break; + } + case TASK_BEHAVIOUR_TIMESHARING: + { + if (prior < TASK_PRIORITY_TIMESHARING_LOW || + prior > TASK_PRIORITY_TIMESHARING_HIGH) + CORE_ESCAPE("the given priority is inconsistent with the " + "behaviour"); + + break; + } + case TASK_BEHAVIOUR_BACKGROUND: + { + if (prior < TASK_PRIORITY_BACKGROUND_LOW || + prior > TASK_PRIORITY_BACKGROUND_HIGH) + CORE_ESCAPE("the given priority is inconsistent with the " + "behaviour"); + + break; + } + default: + { + CORE_ESCAPE("unknown behaviour '%u'", + behav); + } + } + + /* + * 1) + */ + + memset(&o, 0x0, sizeof (o_task)); + + o.class = class; + o.behaviour = behav; + o.priority = prior; + o.as = ID_UNUSED; + o.state = TASK_STATE_STOP; + o.value = WAIT_VALUE_UNKNOWN; + o.timer = ID_UNUSED; + + /* + * 2) + */ + + if (id_reserve(&_task.id, &o.id) != ERROR_OK) + CORE_ESCAPE("unable to reserve an identifier for the task"); + + /* + * 3) + */ + + if (_kernel.task != ID_UNUSED) + { + o_task* parent; + + if (task_current(&o.parent) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the currently running task"); + + if (task_get(o.parent, &parent) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the task object"); + + if (set_add(parent->children, &o.id) != ERROR_OK) + CORE_ESCAPE("unable to add the new task to the list of its parent's " + "children tasks"); + } + else + o.parent = ID_UNUSED; + + /* + * 4) + */ + + if (cpu_select(&o.cpu) != ERROR_OK) + CORE_ESCAPE("unable to select a CPU on which to place the task"); + + /* + * 5) + */ + + if (set_reserve(array, + SET_OPTION_SORT | SET_OPTION_ALLOCATE, + TASK_THREADS_INITSZ, + sizeof (i_thread), + &o.threads) != ERROR_OK) + CORE_ESCAPE("unable to reserve a set of threads"); + + /* + * 6) + */ + + if (set_reserve(array, + SET_OPTION_ALLOCATE, + TASK_WAITS_INITSZ, + sizeof (i_thread), + &o.waits) != ERROR_OK) + CORE_ESCAPE("unable to reserve a set for the waiting tasks/threads"); + + /* + * 7) + */ + + if (set_reserve(ll, + SET_OPTION_SORT | SET_OPTION_ALLOCATE, + sizeof (i_task), + &o.children) != ERROR_OK) + CORE_ESCAPE("unable to reserve a set for the children tasks"); + + /* + * 8) + */ + + if (set_reserve(ll, + SET_OPTION_SORT | SET_OPTION_ALLOCATE, + sizeof (o_message_type), + &o.messages) != ERROR_OK) + CORE_ESCAPE("unable to reserve a set for the messages"); + + /* + * 9) + */ + + if (set_add(_task.tasks, &o) != ERROR_OK) + CORE_ESCAPE("unable to add the object to the set of tasks"); + + /* + * 10) + */ + + *id = o.id; + + /* + * 11) + */ + + if (machine_call(task, reserve, class, behav, prior, id) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function releases a task. + * + * steps: + * + * 1) call the machine. + * 2) retrieve the task object. + * 3) release the task's timer, if necessary. + * 4) flush the task's threads and release the set of threads. + * 5) if the task possesses an address space and this is not the kernel's, + * release it. + * 6) release the set of children. + * 7) release the set of waiting threads. + * 8) flush and release the set of messages. + * 9) release the task identifier. + * 10) remove the task from the manager's set of tasks. + */ + +t_error task_release(i_task id) +{ + o_task* o; + + /* + * 1) + */ + + if (machine_call(task, release, id) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 2) + */ + + if (task_get(id, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the task object"); + + /* + * 3) + */ + + if (o->timer != ID_UNUSED) + { + if (timer_release(o->timer) != ERROR_OK) + CORE_ESCAPE("unable to release the timer"); + } + + /* + * 4) + */ + + if (thread_flush(id) != ERROR_OK) + CORE_ESCAPE("unable to flush the task's threads"); + + if (set_release(o->threads) != ERROR_OK) + CORE_ESCAPE("unable to release the set of threads"); + + /* + * 5) + */ + + if ((o->as != ID_UNUSED) && + (o->as != _kernel.as)) + { + if (as_release(o->as) != ERROR_OK) + CORE_ESCAPE("unable to release the task's address space"); + } + + /* + * 6) + */ + + if (set_release(o->children) != ERROR_OK) + CORE_ESCAPE("unable to release the set of children tasks"); + + /* + * 7) + */ + + if (set_release(o->waits) != ERROR_OK) + CORE_ESCAPE("unable to release the set of waiting tasks/threads"); + + /* + * 8) + */ + + if (message_flush(o->id) != ERROR_OK) + CORE_ESCAPE("unable to flush the messages"); + + if (set_release(o->messages) != ERROR_OK) + CORE_ESCAPE("unable to release the set of messages"); + + /* + * 9) + */ + + if (id_release(&_task.id, o->id) != ERROR_OK) + CORE_ESCAPE("unable to release the task identifier"); + + /* + * 10) + */ + + if (set_remove(_task.tasks, o->id) != ERROR_OK) + CORE_ESCAPE("unable to remove the object from the set of tasks"); + + CORE_LEAVE(); +} + +/* + * this function updates the task's priority. + * + * steps: + * + * 1) retrieve the task object. + * 2) check that the priority lies in the priority range associated with + * this task i.e depending on the task's behaviour. + * 3) update the task's priority. + * 4) go through the task's threads. + * a) retrieve the thread identifier. + * b) retrieve the thread object. + * c) request the scheduler to update the thread. indeed, since the task + * priority has changed, the scheduling priority may have changed as + * well. if it has, the thread may have to be moved to another queue + * for instance. + * 5) call the machine. + */ + +t_error task_priority(i_task id, + t_priority prior) +{ + o_task* o; + s_iterator i; + t_state state; + + /* + * 1) + */ + + if (task_get(id, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + switch (o->behaviour) + { + case TASK_BEHAVIOUR_KERNEL: + { + if (prior < TASK_PRIORITY_KERNEL_LOW || + prior > TASK_PRIORITY_KERNEL_HIGH) + CORE_ESCAPE("the given priority is inconsistent with the " + "behaviour"); + + break; + } + case TASK_BEHAVIOUR_REALTIME: + { + if (prior < TASK_PRIORITY_REALTIME_LOW || + prior > TASK_PRIORITY_REALTIME_HIGH) + CORE_ESCAPE("the given priority is inconsistent with the " + "behaviour"); + + break; + } + case TASK_BEHAVIOUR_INTERACTIVE: + { + if (prior < TASK_PRIORITY_INTERACTIVE_LOW || + prior > TASK_PRIORITY_INTERACTIVE_HIGH) + CORE_ESCAPE("the given priority is inconsistent with the " + "behaviour"); + + break; + } + case TASK_BEHAVIOUR_TIMESHARING: + { + if (prior < TASK_PRIORITY_TIMESHARING_LOW || + prior > TASK_PRIORITY_TIMESHARING_HIGH) + CORE_ESCAPE("the given priority is inconsistent with the " + "behaviour"); + + break; + } + case TASK_BEHAVIOUR_BACKGROUND: + { + if (prior < TASK_PRIORITY_BACKGROUND_LOW || + prior > TASK_PRIORITY_BACKGROUND_HIGH) + CORE_ESCAPE("the given priority is inconsistent with the " + "behaviour"); + + break; + } + default: + CORE_ESCAPE("unknown behaviour '%u'", + o->behaviour); + } + + /* + * 3) + */ + + o->priority = prior; + + /* + * 4) + */ + + set_foreach(SET_OPTION_FORWARD, o->threads, &i, state) + { + i_thread* th; + o_thread* oth; + + /* + * a) + */ + + if (set_object(o->threads, i, (void**)&th) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread identifier"); + + /* + * b) + */ + + if (thread_get(*th, &oth) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread object"); + + /* + * c) + */ + + if ((o->state == TASK_STATE_START) && + (oth->state == THREAD_STATE_START)) + { + if (scheduler_update(*th) != ERROR_OK) + CORE_ESCAPE("unable to update the thread scheduling state"); + } + } + + /* + * 5) + */ + + if (machine_call(task, priority, id, prior) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function starts the task. + * + * steps: + * + * 1) retrieve the task object. + * 2) return an error if the thread is already running. + * 3) set the task's state as started. + * 4) go through the waiting threads. + * a) retrieve the thread identifier. + * b) retrieve the thread object. + * c) if the thread is waiting for the task to be started. + * i) set the wake-up information: the task has started. + * ii) wake up the waiting thread. + * iii) delete the thread from the waiting set. + * 5) go through the threads + * a) retrieve the thread identifier. + * b) retrieve the thread object. + * c) if the thread has been started but is not scheduled because the + * task was not started---i.e it is now the case---add the thread + * to the scheduler. + * 6) call the machine. + */ + +t_error task_start(i_task id) +{ + o_task* object; + s_iterator i; + t_state s; + + /* + * 1) + */ + + if (task_get(id, &object) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + if (object->state == TASK_STATE_START) + CORE_ESCAPE("a task cannot be started twice"); + + /* + * 3) + */ + + object->state = TASK_STATE_START; + + /* + * 4) + */ + + try: + set_foreach(SET_OPTION_FORWARD, object->waits, &i, s) + { + i_thread* id; + o_thread* o; + + /* + * a) + */ + + if (set_object(object->waits, i, (void**)&id) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the waiting thread's identifier"); + + /* + * b) + */ + + if (thread_get(*id, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the waiting thread"); + + /* + * c) + */ + + if (o->wait.state & WAIT_STATE_START) + { + /* + * i) + */ + + o->wait.cause = WAIT_STATE_START; + o->wait.value = WAIT_VALUE_UNKNOWN; + + /* + * ii) + */ + + if (thread_start(o->id) != ERROR_OK) + CORE_ESCAPE("unable to start the waiting thread"); + + /* + * iii) + */ + + if (set_delete(object->waits, i) != ERROR_OK) + CORE_ESCAPE("unable to delete the thread from the waiting list"); + + goto try; + } + } + + /* + * 5) + */ + + set_foreach(SET_OPTION_FORWARD, object->threads, &i, s) + { + i_thread* id; + o_thread* o; + + /* + * a) + */ + + if (set_object(object->threads, i, (void**)&id) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread identifier"); + + /* + * b) + */ + + if (thread_get(*id, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread object"); + + /* + * c) + */ + + if (o->state == THREAD_STATE_START) + { + if (scheduler_add(o->id) != ERROR_OK) + CORE_ESCAPE("unable to add the thread to the scheduler"); + } + } + + /* + * 6) + */ + + if (machine_call(task, start, id) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function stops a task. + * + * steps: + * + * 1) retrieve the task object. + * 2) return an error if the thread is already stopped. + * 3) save the current task's state. + * 4) set the task's state as stopped. + * 5) go through the waiting threads. + * a) retrieve the thread identifier. + * b) retrieve the thread object. + * c) if the thread is waiting for the task to be stopped. + * i) set the wake-up information: the task has stopped. + * ii) wake up the waiting thread. + * iii) delete the thread from the waiting set. + * 6) go through the threads + * a) retrieve the thread identifier. + * b) retrieve the thread object. + * c) if the thread is running and the task was, stop it by removing + * the thread from the scheduler. + * 7) call the machine. + */ + +t_error task_stop(i_task id) +{ + o_task* object; + t_state state; + s_iterator i; + t_state s; + + /* + * 1) + */ + + if (task_get(id, &object) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + if (object->state == TASK_STATE_STOP) + CORE_ESCAPE("a task cannot be stopped twice"); + + /* + * 3) + */ + + state = object->state; + + /* + * 4) + */ + + object->state = TASK_STATE_STOP; + + /* + * 5) + */ + + try: + set_foreach(SET_OPTION_FORWARD, object->waits, &i, s) + { + i_thread* id; + o_thread* o; + + /* + * a) + */ + + if (set_object(object->waits, i, (void**)&id) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the waiting thread's identifier"); + + /* + * b) + */ + + if (thread_get(*id, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the waiting thread"); + + /* + * c) + */ + + if (o->wait.state & WAIT_STATE_STOP) + { + /* + * i) + */ + + o->wait.cause = WAIT_STATE_STOP; + o->wait.value = WAIT_VALUE_UNKNOWN; + + /* + * ii) + */ + + if (thread_start(o->id) != ERROR_OK) + CORE_ESCAPE("unable to start the waiting thread"); + + /* + * iii) + */ + + if (set_delete(object->waits, i) != ERROR_OK) + CORE_ESCAPE("unable to delete the thread from the waiting list"); + + goto try; + } + } + + /* + * 6) + */ + + set_foreach(SET_OPTION_FORWARD, object->threads, &i, s) + { + i_thread* id; + o_thread* o; + + /* + * a) + */ + + if (set_object(object->threads, i, (void**)&id) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread identifier"); + + /* + * b) + */ + + if (thread_get(*id, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread object"); + + /* + * c) + */ + + if ((state == TASK_STATE_START) && + (o->state == THREAD_STATE_START)) + { + if (scheduler_remove(o->id) != ERROR_OK) + CORE_ESCAPE("unable to add the thread to the scheduler"); + } + } + + /* + * 7) + */ + + if (machine_call(task, stop, id) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function block the task waiting for an event to wake it up. + * + * steps: + * + * 1) retrieve the task object. + * 2) return an error if the thread is already blocked. + * 3) save the current task's state. + * 4) set the task's state as blocked. + * 5) go through the threads + * a) retrieve the thread identifier. + * b) retrieve the thread object. + * c) if the thread is running and the task was running, stop it + * by removing the thread from the scheduler. + * 6) call the machine. + */ + +t_error task_block(i_task id) +{ + o_task* object; + t_state state; + s_iterator i; + t_state s; + + /* + * 1) + */ + + if (task_get(id, &object) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + if (object->state == TASK_STATE_BLOCK) + CORE_ESCAPE("a task cannot be blocked twice"); + + /* + * 3) + */ + + state = object->state; + + /* + * 4) + */ + + object->state = TASK_STATE_BLOCK; + + /* + * 5) + */ + + set_foreach(SET_OPTION_FORWARD, object->threads, &i, s) + { + i_thread* id; + o_thread* o; + + /* + * a) + */ + + if (set_object(object->threads, i, (void**)&id) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread identifier"); + + /* + * b) + */ + + if (thread_get(*id, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread object"); + + /* + * c) + */ + + if ((state == TASK_STATE_START) && + (o->state == THREAD_STATE_START)) + { + if (scheduler_remove(o->id) != ERROR_OK) + CORE_ESCAPE("unable to remove the thread from the scheduler"); + } + } + + /* + * 6) + */ + + if (machine_call(task, block, id) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function exits a task, provided an exit value. + * + * the idea behind this function is to (i) exit the task's threads (ii) wake + * up the threads waiting for the task's death (iii) mark the task's + * children as being orphans and, finally (iv) put the task to the morgue + * if someone has taken notice of its death. + * + * note that the threads are exited before the task changes state. indeed, + * assuming the task was running before exiting, the running threads would + * not be removed from the scheduler if exited once the task has been marked + * as dying. + * + * besides, a special case is made for the currently executing thread. + * should this thread belong to the exiting task, it will not be exited + * with the others. indeed, if this thread is exited, hence removed from the + * scheduler, the execution will be yielded and the task exiting process + * will hang in this state forever. therefore, this special thread is exited + * at the very end of the function, hence leading the execution to be yielded + * without interferring with the current operation since having been completed + * successfully. + * + * in addition, this function performs a quite unusual operation. the + * morgue, in which the dead tasks are stored, relies on a timer. + * unfortunately, the timer manager must be initialized after the task manager. + * therefore the task manager could not reserve a timer in its initialization + * phase. the timer reservation has therefore been put in this function such + * that, if no task dies, the timer is never reserved and the morgue never + * run. + * + * steps: + * + * ~) if required, reserve the timer associated with the morgue. + * 1) retrieve the task object. + * 2) if the task is already dying or dead, return an error. + * 3) retrieve the currently running thread identifier. + * 4) store the exit value in the task object. + * 5) exit the task's threads. + * a) retrieve the thread identifier and object. + * b) perform the exit depending on the nature of the thread. + * A) if the thread to exit is currently executing... + * i) save this special thread identifier so that to exit it at the + * very end since its exit is very likely to lead to a yield. + * B) otherwise... + * i) if the thread is already dying or dead, needless to exit it as it + * already has. + * ii) exit the thread with the exit value provided through the + * task_exit(). + * 6) change the task's state as being dying i.e zombie. + * 7) go through the waiting threads. + * a) retrieve the waiting thread identifier. + * b) retrieve the thread object. + * c) if the thread is waiting for the task's death. + * i) set its waiting result. + * ii) wake up the thread. + * iii) remove the thread from set of waiting threads. + * iv) mark the fact that at least one thread has taken notice of the + * task's death i.e the task can now be buried. + * v) since the set has been modified i.e iterators may no longer be + * consistent, re-start from the beginning. + * 8) if at least one thread has taken notice of the death, bury the task. + * a) set the task's state to dead. + * b) add the task identifier to the morgue for actual burial. + * 9) go through the task's children so as to make them orphans. + * a) retrieve the task identifier. + * b) retrieve the task object. + * c) re-initialize the parent field, hence making the task an orphan. + * 10) call the machine. + * 11) finally, if the currently executing thread has been detecting as + * belonging to the dying task, exit it. + * a) exit the thread. note however that since the task is in a zombie + * or dead state, the thread_exit() function will consider that the + * thread is not currently in the scheduler's data structures. therefore + * the thread will not be removed from the scheduler. + * b) since it should, perform the scheduler removal manually. + */ + +t_error task_exit(i_task id, + t_value value) +{ + o_task* object; + i_thread current; + i_thread thread; + s_iterator i; + t_state s; + t_boolean w; + + /* + * ~) + */ + + if (_task.morgue.timer == ID_UNUSED) + { + if (timer_reserve(TIMER_TYPE_FUNCTION, + TIMER_ROUTINE(task_bury), + TIMER_DATA(NULL), + TASK_MORGUE_DELAY, + TIMER_OPTION_REPEAT, + &_task.morgue.timer) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve the timer"); + } + + /* + * 1) + */ + + if (task_get(id, &object) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + if ((object->state == TASK_STATE_ZOMBIE) || + (object->state == TASK_STATE_DEAD)) + CORE_ESCAPE("a task cannot be exited twice"); + + /* + * 3) + */ + + if (thread_current(¤t) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the currently executing thread"); + + /* + * 4) + */ + + object->value = value; + + /* + * 5) + */ + + thread = ID_UNUSED; + + set_foreach(SET_OPTION_FORWARD, object->threads, &i, s) + { + i_thread* id; + o_thread* o; + + /* + * a) + */ + + if (set_object(object->threads, i, (void**)&id) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread identifier"); + + if (thread_get(*id, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread object"); + + /* + * b) + */ + + if (o->id == current) + { + /* + * A) + */ + + /* + * i) + */ + + thread = o->id; + } + else + { + /* + * B) + */ + + /* + * i) + */ + + if ((o->state == THREAD_STATE_ZOMBIE) || + (o->state == THREAD_STATE_DEAD)) + continue; + + /* + * ii) + */ + + if (thread_exit(o->id, object->value) != ERROR_OK) + CORE_ESCAPE("unable to exit the thread"); + } + } + + /* + * 6) + */ + + object->state = TASK_STATE_ZOMBIE; + + /* + * 7) + */ + + w = BOOLEAN_FALSE; + + try: + set_foreach(SET_OPTION_FORWARD, object->waits, &i, s) + { + i_thread* id; + o_thread* o; + + /* + * a) + */ + + if (set_object(object->waits, i, (void**)&id) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the waiting thread's identifier"); + + /* + * b) + */ + + if (thread_get(*id, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the waiting thread"); + + /* + * c) + */ + + if (o->wait.state & WAIT_STATE_DEATH) + { + /* + * i) + */ + + o->wait.cause = WAIT_STATE_DEATH; + o->wait.value = object->value; + + /* + * ii) + */ + + if (thread_start(o->id) != ERROR_OK) + CORE_ESCAPE("unable to start the waiting thread"); + + /* + * iii) + */ + + if (set_delete(object->waits, i) != ERROR_OK) + CORE_ESCAPE("unable to delete the thread from the waiting list"); + + /* + * iv) + */ + + w = BOOLEAN_TRUE; + + /* + * v) + */ + + goto try; + } + } + + /* + * 8) + */ + + if (w == BOOLEAN_TRUE) + { + /* + * a) + */ + + object->state = THREAD_STATE_DEAD; + + /* + * b) + */ + + if (set_add(_task.morgue.field, &object->id) != ERROR_OK) + CORE_ESCAPE("unable to add the task to the morgue"); + } + + /* + * 9) + */ + + set_foreach(SET_OPTION_FORWARD, object->children, &i, s) + { + i_task* child; + o_task* o; + + /* + * a) + */ + + if (set_object(object->children, i, (void**)&child) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the task identifier"); + + /* + * b) + */ + + if (task_get(*child, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the task object"); + + /* + * c) + */ + + o->parent = ID_UNUSED; + } + + /* + * 10) + */ + + if (machine_call(task, exit, id, value) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 11) + */ + + if (thread != ID_UNUSED) + { + /* + * a) + */ + + if (thread_exit(thread, object->value) != ERROR_OK) + CORE_ESCAPE("unable to exit the currently running thread"); + + /* + * b) + */ + + if (scheduler_remove(thread) != ERROR_OK) + CORE_ESCAPE("unable to remove the thread from the scheduler"); + } + + CORE_LEAVE(); +} + +/* + * this function is triggered on a regular basis in order to bury the + * dead tasks. + * + * steps: + * + * 1) go through the dead tasks. + * a) retrieve the task identifier and object. + * b) if the task is not dead, it should not be here: this is a fatal error! + * c) release the task. + * d) remove the task identifier from the morgue. + */ + +void task_bury(i_timer timer, + t_vaddr data) +{ + s_iterator i; + + /* + * 1) + */ + + while (set_head(_task.morgue.field, &i) == ERROR_TRUE) + { + i_task* task; + o_task* object; + + /* + * a) + */ + + assert(set_object(_task.morgue.field, i, (void**)&task) == ERROR_OK); + + assert(task_get(*task, &object) == ERROR_OK); + + /* + * b) + */ + + assert(object->state == TASK_STATE_DEAD); + + /* + * c) + */ + + assert(task_release(object->id) == ERROR_OK); + + /* + * d) + */ + + assert(set_delete(_task.morgue.field, i) == ERROR_OK); + } +} + +/* + * this function provies the thread 'id' to wait for the target task to change + * its state to 'state'. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the thread and target task objects. + * 2) check if the thread willing to wait is running. + * 3) if the thread is waiting for the task to start and the task is already + * running, fill and return the information. + * 4) if the thread is waiting for the task to stop and the task is already + * stopped, fill and return the information. + * 5) if the thread is waiting for the task to exit and the task is already + * dying, fill and return the information. + * a) fill the waiting thread's wait structure. + * b) set the task as being dead now that someone has taken notice + * of its death. + * c) add the task to the morgue. + * 6) if the task is already dead, there is nothing to wait for, return an + * error as it is too late. + * 7) set the thread's waiting state as the one provided. it is necessary + * to store such an information for later when the task will finally + * change its state. at that moment, the task will have to know what + * the thread is waiting for to decide wheter to wake him up or not. + * 8) add the thread to the task's waiting list. + * 9) stop the thread. the thread will be woken up by the task once it will + * have changed to the given state. therefore the next steps are executed + * only after the thread has been woken up. + * 10) retrieve the information passed by the task on the event that led + * to the thread being woken up i.e the cause but also the value should + * the task have exited. note that these information are passed by the + * task through the thread's object 'wait' specific attribute. + * 11) re-initialize the thread has being waiting for nothing. + * 12) call the machine. + */ + +t_error task_wait(i_thread id, + i_task target, + t_state state, + s_wait* wait) +{ + o_thread* object; + o_task* o; + + /* + * 0) + */ + + if (wait == NULL) + CORE_ESCAPE("the 'wait' argument is null"); + + /* + * 1) + */ + + if (thread_get(id, &object) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread object"); + + if (task_get(target, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread object"); + + /* + * 2) + */ + + if (object->state != THREAD_STATE_START) + CORE_ESCAPE("unable to make a non-running thread wait for a task"); + + /* + * 3) + */ + + if ((state & WAIT_STATE_START) && + (o->state == TASK_STATE_START)) + { + wait->id.task = target; + wait->state = state; + wait->cause = WAIT_STATE_START; + wait->value = WAIT_VALUE_UNKNOWN; + + CORE_LEAVE(); + } + + /* + * 4) + */ + + if ((state & WAIT_STATE_STOP) && + (o->state == TASK_STATE_STOP)) + { + wait->id.task = target; + wait->state = state; + wait->cause = WAIT_STATE_STOP; + wait->value = WAIT_VALUE_UNKNOWN; + + CORE_LEAVE(); + } + + /* + * 5) + */ + + if ((state & WAIT_STATE_DEATH) && + (o->state == TASK_STATE_ZOMBIE)) + { + /* + * a) + */ + + wait->id.task = target; + wait->state = state; + wait->cause = WAIT_STATE_DEATH; + wait->value = o->value; + + /* + * b) + */ + + o->state = THREAD_STATE_DEAD; + + /* + * c) + */ + + if (set_add(_task.morgue.field, &o->id) != ERROR_OK) + CORE_ESCAPE("unable to add the task to the morgue"); + + CORE_LEAVE(); + } + + /* + * 6) + */ + + if (o->state == TASK_STATE_DEAD) + CORE_ESCAPE("unable to wait for a dead task"); + + /* + * 7) + */ + + object->wait.state = state; + + /* + * 8) + */ + + if (set_add(o->waits, &id) != ERROR_OK) + CORE_ESCAPE("unable to add the thread identifier to the waiting list"); + + /* + * 9) + */ + + if (thread_stop(id) != ERROR_OK) + CORE_ESCAPE("unable to stop the task"); + + /* + * 10) + */ + + wait->id.task = target; + wait->state = object->wait.state; + wait->cause = object->wait.cause; + wait->value = object->wait.value; + + /* + * 11) + */ + + object->wait.state = WAIT_STATE_NONE; + + /* + * 12) + */ + + if (machine_call(task, wait, id, target, state, wait) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function is called when a task must be woken up. + * + * steps: + * + * 1) retrieve the task object. + * 2) re-initialize the task's timer. + * 3) wake up the task. + */ + +void task_wakeup(i_timer timer, + t_vaddr data) +{ + i_task* id = (i_task*)data; + o_task* o; + + /* + * 1) + */ + + assert(task_get(*id, &o) == ERROR_OK); + + /* + * 2) + */ + + o->timer = ID_UNUSED; + + /* + * 3) + */ + + assert(task_start(o->id) == ERROR_OK); +} + +/* + * this function puts the given task to sleep. this operation implies + * suspending the execution of all the task's threads. + * + * note that every task object embeds an available timer identifier which, + * in this case, is used to trigger the wakeup function. + * + * another particularity comes from the fact that the id of the task to + * wake up is passed as the data to the timer. note that since the object + * is not supposed to be released before the timer is triggered, the + * address of the identifier within the task object is actually passed. + * + * steps: + * + * 1) retrieve the task object. + * 2) if the task is not running, it cannot be put to sleep. + * 3) reserve a timer. + * 4) call the machine. + * 5) finally, block the task. + */ + +t_error task_sleep(i_task id, + t_delay milliseconds) +{ + o_task* o; + + /* + * 1) + */ + + if (task_get(id, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + if (o->state != TASK_STATE_START) + CORE_ESCAPE("unable to put to sleep a non-running task"); + + /* + * 3) + */ + + if (timer_reserve(TIMER_TYPE_FUNCTION, + TIMER_ROUTINE(task_wakeup), + TIMER_DATA(&o->id), + milliseconds, + TIMER_OPTION_NONE, + &o->timer) != ERROR_OK) + CORE_ESCAPE("unable to reserve a timer"); + + /* + * 4) + */ + + if (machine_call(task, sleep, id, milliseconds) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 5) + */ + + if (task_block(o->id) != ERROR_OK) + CORE_ESCAPE("unable to block the task"); + + CORE_LEAVE(); +} + +/* + * this function returns the task identifier of the thread being executed + * on the current CPU. + * + * steps: + * + * 0) verify the arguments + * 1) retrieve the scheduler for the current CPU. + * 2) retrieve the thread object being executed. + * 3) return the task identifier of the thread. + */ + +t_error task_current(i_task* task) +{ + o_scheduler* scheduler; + o_thread* thread; + + /* + * 0) + */ + + if (task == NULL) + CORE_ESCAPE("the 'task' argument is null"); + + /* + * 1) + */ + + if (scheduler_current(&scheduler) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the current scheduler"); + + /* + * 2) + */ + + if (thread_get(scheduler->thread, &thread) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread object"); + + /* + * 3) + */ + + *task = thread->task; + + CORE_LEAVE(); +} + +/* + * this function returns true if the task object exists. + */ + +t_error task_exist(i_task id) +{ + if (set_exist(_task.tasks, id) != ERROR_TRUE) + CORE_FALSE(); + + CORE_TRUE(); +} + +/* + * this function retrieve a task object given its identifier. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the object from the set of tasks. + */ + +t_error task_get(i_task id, + o_task** object) +{ + /* + * 0) + */ + + if (object == NULL) + CORE_ESCAPE("the 'object' argument is null"); + + /* + * 1) + */ + + if (set_get(_task.tasks, id, (void**)object) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the object from the set of tasks"); + + CORE_LEAVE(); +} + +/* + * this function initializes the task manager. + * + * note that this function takes care of reserving and initializing + * the kernel task, address space and thread but also to inject the + * pre-reserved segments and regions provided by the boot loader. + * + * finally, let us recall that the kernel will, later on, spawn the very + * first server referred to as 'system'. this server is located somewhere + * in a pre-allocated area of memory. this function also takes care to + * identifies the segment which contains the server's code so that it can get + * mapped and launched properly once the kernel is set up. + * + * steps: + * + * 1) display the message. + * 2) initialize the task manager's structure. + * 3) build the task manager's identifier object. + * 4) reserve the set of tasks. + * 5) reserve the set of dead tasks i.e the morgue. + * 6) since the morgue timer cannot be reserved at the time, set the identifier + * as being unused and defer the reservation to the first task exiting i.e + * for more information, please refer to the task_exit() function. + * 7) reserve the kernel task. + * 8) reserve the kernel address space. + * 9) start the kernel task. + * 10) go through the pre-reserved segments. + * a) ignore zero-sized segments. + * b) allocate a segment object. + * c) fill the segment object's attributes. + * d) inject the segment in the kernel's address space. + * e) if the current segment corresponds to the one hosting the 'system' + * server's code, save the segment identifier in the '_system' variable. + * 11) go through the pre-reserved regions. + * a) allocate a region object. + * b) fill the region object's attributes. + * c) inject the region in the kernel's address space. + * 12) call the machine. + */ + +t_error task_initialize(void) +{ + i_segment segments[_init->nsegments]; + i_region useless; + o_segment* segment; + o_region* region; + t_uint32 i; + + /* + * 1) + */ + + module_call(console, message, + '+', "initializing the task manager\n"); + + /* + * 2) + */ + + memset(&_task, 0x0, sizeof (m_task)); + + /* + * 3) + */ + + if (id_build(&_task.id) != ERROR_OK) + CORE_ESCAPE("unable to build the identifier object"); + + /* + * 4) + */ + + if (set_reserve(ll, + SET_OPTION_ALLOCATE | SET_OPTION_SORT, + sizeof (o_task), + &_task.tasks) != ERROR_OK) + CORE_ESCAPE("unable to reserve the set of tasks"); + + /* + * 5) + */ + + if (set_reserve(ll, + SET_OPTION_ALLOCATE | SET_OPTION_SORT, + sizeof (i_task), + &_task.morgue.field) != ERROR_OK) + CORE_ESCAPE("unable to reserve the set of dead tasks i.e the morgue"); + + /* + * 6) + */ + + _task.morgue.timer = ID_UNUSED; + + /* + * 7) + */ + + if (task_reserve(TASK_CLASS_KERNEL, + TASK_BEHAVIOUR_KERNEL, + TASK_PRIORITY_KERNEL, + &_kernel.task) != ERROR_OK) + CORE_ESCAPE("unable to reserve the kernel task"); + + /* + * 8) + */ + + if (as_reserve(_kernel.task, + &_kernel.as) != ERROR_OK) + CORE_ESCAPE("unable to reserve the kernel address space"); + + /* + * 9) + */ + + if (task_start(_kernel.task) != ERROR_OK) + CORE_ESCAPE("unable to start the kernel task"); + + /* + * 10) + */ + + for (i = 0; i < _init->nsegments; i++) + { + /* + * a) + */ + + if (_init->segments[i].size == 0) + continue; + + /* + * b) + */ + + if ((segment = malloc(sizeof (o_segment))) == NULL) + CORE_ESCAPE("unable to allocate memory for the segment object"); + + /* + * c) + */ + + segment->address = _init->segments[i].address; + segment->size = _init->segments[i].size; + segment->permissions = _init->segments[i].permissions; + segment->options = _init->segments[i].options; + + /* + * d) + */ + + if (segment_inject(_kernel.as, segment, &segments[i]) != ERROR_OK) + CORE_ESCAPE("unable to inject the segment object pre-reserved " + "by the boot loader"); + + /* + * e) + */ + + if (_init->scode == _init->segments[i].address) + _system = segments[i]; + } + + /* + * 11) + */ + + for (i = 0; i < _init->nregions; i++) + { + /* + * a) + */ + + if ((region = malloc(sizeof (o_region))) == NULL) + CORE_ESCAPE("unable to allocate memory for the region object"); + + /* + * b) + */ + + region->segment = segments[_init->regions[i].segment]; + region->address = _init->regions[i].address; + region->offset = _init->regions[i].offset; + region->size = _init->regions[i].size; + region->options = _init->regions[i].options; + + /* + * c) + */ + + if (region_inject(_kernel.as, region, &useless) != ERROR_OK) + CORE_ESCAPE("unable to inject the region object pre-reserved " + "by the boot loader"); + } + + /* + * 12) + */ + + if (machine_call(task, initialize) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function cleans the task manager. + * + * steps: + * + * 1) display a message. + * 2) call the machine + * 3) go through the threads. + * a) retrieve the task identifier. + * b) release the task. + * 4) release the morgue's timer, if necessary. for more information, pleae + * refer to task_initialize(). + * 5) release the morgue's set. + * 6) release the set of tasks. + * 7) release the identifier object. + */ + +t_error task_clean(void) +{ + s_iterator i; + t_state s; + + /* + * 1) + */ + + module_call(console, message, + '+', "cleaning the task manager\n"); + + /* + * 2) + */ + + if (machine_call(task, clean) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 3) + */ + + set_foreach(SET_OPTION_FORWARD, _task.tasks, &i, s) + { + i_task* task; + + /* + * a) + */ + + if (set_object(_task.tasks, i, (void**)&task) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the task identifier"); + + /* + * b) + */ + + if (task_release(*task) != ERROR_OK) + CORE_ESCAPE("unable to release the task"); + } + + /* + * 4) + */ + + if (_task.morgue.timer != ID_UNUSED) + { + if (timer_release(_task.morgue.timer) != ERROR_OK) + CORE_ESCAPE("unable to release the timer"); + } + + /* + * 5) + */ + + if (set_release(_task.morgue.field) != ERROR_OK) + CORE_ESCAPE("unable to release the set of dead tasks"); + + /* + * 6) + */ + + if (set_release(_task.tasks) != ERROR_OK) + CORE_ESCAPE("unable to release the set of tasks"); + + /* + * 7) + */ + + if (id_destroy(&_task.id) != ERROR_OK) + CORE_ESCAPE("unable to destroy the identifier object"); + + CORE_LEAVE(); +} diff --git a/kaneton/core/thread/Makefile b/kaneton/core/thread/Makefile new file mode 100644 index 0000000..280410b --- /dev/null +++ b/kaneton/core/thread/Makefile @@ -0,0 +1,76 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/buckman/kaneton/kaneton/core/thread/Makefile +# +# created julien quintard [sun jun 10 17:10:24 2007] +# updated matthieu bucchianeri [sat sep 1 12:29:27 2007] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/core/thread + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +THREAD_C := thread.c + +THREAD_O := $(THREAD_C:.c=.o) + +THREAD_INCLUDE := $(_CORE_INCLUDE_DIR_)/thread.h + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(_THREAD_LO_) + +$(_THREAD_LO_): $(THREAD_O) + $(call env_remove,$(_THREAD_LO_),) + + $(call env_archive,$(_THREAD_LO_),$(THREAD_O),) + +clear: + $(call env_remove,$(THREAD_O),) + + $(call env_remove,$(_THREAD_LO_),) + + $(call env_purge,) + +prototypes: + $(call env_prototypes,$(THREAD_INCLUDE),) + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(THREAD_C),) + +dependencies: + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/core/thread/thread.c b/kaneton/core/thread/thread.c new file mode 100644 index 0000000..48a18ae --- /dev/null +++ b/kaneton/core/thread/thread.c @@ -0,0 +1,1953 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/thread/thread.c + * + * created renaud voltz [tue apr 4 03:02:57 2006] + * updated julien quintard [fri apr 8 09:37:24 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * the thread manager provides functionalities for creating, destorying + * and controlling the active scheduling entites known as _thread_s. + * + * a thread contains an execution context composed of several machine-dependent + * registers which capture the state of a state so that the thread's + * execution can be resumed later. + * + * note that every task possesses its own stack i.e a personal memory area + * which is used to store local information such as arguments and local + * variables. + * + * the thread manager, in addition to creating and destroying threads, + * provides functions for controlling the state of these objects. thus, + * one may want to start a thread so that its code start executing, + * block a thread until some event wakes it up or even exit a thread, + * hence actually destroying the object and return a value to other threads + * which may have been waiting for the thread to do so. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * include the machine-specific definitions required by the core. + */ + +machine_include(thread); + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the init structure. + */ + +extern s_init* _init; + +/* + * the kernel manager. + */ + +extern m_kernel _kernel; + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the thread manager. + */ + +m_thread _thread; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function shows a given thread's attributes. + * + * steps: + * + * 1) retrieve the thread object. + * 2) build the state string. + * 3) display the object's attributes. + * 4) if the thread is currently waiting, display its waiting state. + * a) build the state string. + * b) display the waiting state. + * 5) call the machine. + */ + +t_error thread_show(i_thread threadid, + mt_margin margin) +{ + char* state; + o_thread* o; + + /* + * 1) + */ + + if (thread_get(threadid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread object"); + + /* + * 2) + */ + + switch (o->state) + { + case THREAD_STATE_START: + { + state = "start"; + + break; + } + case THREAD_STATE_STOP: + { + state = "stop"; + + break; + } + case THREAD_STATE_BLOCK: + { + state = "block"; + + break; + } + case THREAD_STATE_ZOMBIE: + { + state = "zombie"; + + break; + } + case THREAD_STATE_DEAD: + { + state = "dead"; + + break; + } + default: + CORE_ESCAPE("unknown thread state '%u'", + o->state); + } + + /* + * 3) + */ + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + "thread:\n", + MODULE_CONSOLE_MARGIN_VALUE(margin)); + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " core: id(%qd) priority(%u) state(%s) waits(%qd) " + "value(%d) entry(0x%x) timer(%qd) task(%qd)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + o->id, + o->priority, + state, + o->waits, + o->value, + o->entry, + o->timer, + o->task); + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " stack: base(0x%x) size(0x%x)", + MODULE_CONSOLE_MARGIN_VALUE(margin), + o->stack.base, + o->stack.size); + + /* + * 4) + */ + + if (o->wait.state != WAIT_STATE_NONE) + { + /* + * a) + */ + + switch (o->wait.state) + { + case WAIT_STATE_START: + { + state = "start"; + + break; + } + case WAIT_STATE_STOP: + { + state = "stop"; + + break; + } + case WAIT_STATE_DEATH: + { + state = "death"; + + break; + } + default: + CORE_ESCAPE("unknown waiting state '%u'", + o->state); + } + + /* + * b) + */ + + module_call(console, print, + " wait(%s)\n", + state); + } + else + module_call(console, print, "\n"); + + /* + * 5) + */ + + if (machine_call(thread, show, threadid, margin) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function dumps the thread manager. + * + * steps: + * + * 1) display general information on the manager. + * 2) show the identifier object. + * 3) retrieve the size of the set of threads. + * 4) display information on the set and show every thread. + * 5) retrieve the size of the set of dead threads. + * 6) display information and show every dead thread identifier. + * 7) call the machine. + */ + +t_error thread_dump(void) +{ + t_setsz size; + s_iterator i; + t_state s; + + /* + * 1) + */ + + module_call(console, message, + '#', "thread manager:\n"); + + module_call(console, message, + '#', " core: threads(%qd)\n", + _thread.threads); + + /* + * 2) + */ + + if (id_show(&_thread.id, + 2 * MODULE_CONSOLE_MARGIN_SHIFT) != ERROR_OK) + CORE_ESCAPE("unable to show the identifier object"); + + /* + * 3) + */ + + if (set_size(_thread.threads, &size) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the size of the set of threads"); + + /* + * 4) + */ + + module_call(console, message, + '#', " threads: id(%qd) size(%qd)\n", + _thread.threads, + size); + + set_foreach(SET_OPTION_FORWARD, _thread.threads, &i, s) + { + o_thread* o; + + if (set_object(_thread.threads, i, (void**)&o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread identifier"); + + if (thread_show(o->id, + 3 * MODULE_CONSOLE_MARGIN_SHIFT) != ERROR_OK) + CORE_ESCAPE("unable to show the thread"); + } + + /* + * 5) + */ + + if (set_size(_thread.morgue.field, &size) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the size of the set of dead threads"); + + /* + * 6) + */ + + module_call(console, message, + '#', " morgue: field(%qd) timer(%qd)\n", + _thread.morgue.field, + _thread.morgue.timer); + + module_call(console, message, + '#', " field: id(%qd) size(%qd)\n", + _thread.morgue.field, + size); + + set_foreach(SET_OPTION_FORWARD, _thread.morgue.field, &i, s) + { + i_thread* id; + + if (set_object(_thread.morgue.field, i, (void**)&id) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread identifier"); + + module_call(console, message, + '#', " thread: id(%qd)\n", + *id); + } + + /* + * 7) + */ + + if (machine_call(thread, dump) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function reserves a thread with the given priority. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the task object. + * 2) reserve a thread identifier. + * 3) initialize and fill the object. + * 4) set up the thread stack depending on the arguments. + * A) if no stack is provided... + * a) check the stack size. + * b) set the stack size. + * c) allocate a stack. + * B) otherwise, use the provided stack address and size. + * 5) reserve a set for the waiting threads. + * 6) add the object to the set of threads. + * 7) add the thread identifier to the task's set of threads. + * 8) call the machine. + */ + +t_error thread_reserve(i_task taskid, + t_priority prior, + t_vaddr stack, + t_vsize stacksz, + t_vaddr entry, + i_thread* id) +{ + o_task* task; + o_thread o; + + /* + * 0) + */ + + if (id == NULL) + CORE_ESCAPE("the 'id' argument is null"); + + if ((prior < THREAD_PRIORITY_LOW) || + (prior > THREAD_PRIORITY_HIGH)) + CORE_ESCAPE("invalid priority"); + + /* + * 1) + */ + + if (task_get(taskid, &task) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + if (id_reserve(&_thread.id, id) != ERROR_OK) + CORE_ESCAPE("unable to reserve a thread identifier"); + + /* + * 3) + */ + + memset(&o, 0x0, sizeof (o_thread)); + + o.id = *id; + o.task = taskid; + o.priority = prior; + o.state = THREAD_STATE_STOP; + o.waits = ID_UNUSED; + o.value = WAIT_VALUE_UNKNOWN; + o.entry = entry; + o.timer = ID_UNUSED; + + o.wait.state = WAIT_STATE_NONE; + + /* + * 4) + */ + + if (stack == THREAD_STACK_ADDRESS_NONE) + { + /* + * A) + */ + + /* + * a) + */ + + if (stacksz < THREAD_STACK_SIZE_LOW) + CORE_ESCAPE("the provided stack size is too low"); + + /* + * b) + */ + + o.stack.size = stacksz; + + /* + * c) + */ + + if (map_reserve(task->as, + (task->class == TASK_CLASS_KERNEL ? + MAP_OPTION_SYSTEM : MAP_OPTION_NONE), + o.stack.size, + PERMISSION_READ | PERMISSION_WRITE, + &(o.stack.base)) != ERROR_OK) + CORE_ESCAPE("unable to reserve a map for the thread's stack"); + } + else + { + /* + * B) + */ + + o.stack.base = stack; + o.stack.size = stacksz; + } + + /* + * 5) + */ + + if (set_reserve(array, + SET_OPTION_ALLOCATE, + THREAD_WAITS_INITSZ, + sizeof (i_thread), + &o.waits) != ERROR_OK) + CORE_ESCAPE("unable to reserve a set for the waiting tasks/threads"); + + /* + * 6) + */ + + if (set_add(_thread.threads, &o) != ERROR_OK) + CORE_ESCAPE("unable to add the object to the set of threads"); + + /* + * 7) + */ + + if (set_add(task->threads, &o.id) != ERROR_OK) + CORE_ESCAPE("unable to add the thread to the task's set of threads"); + + /* + * 8) + */ + + if (machine_call(thread, reserve, + taskid, prior, stack, stacksz, entry, id) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function releases a thread. + * + * steps: + * + * 1) call the machine. + * 2) retrieve the thread object. + * 3) retrieve the task object. + * 4) if the thread possesses a stack, release it. + * 5) if necessary, release the thread's timer. + * 6) release the thread identifier. + * 7) remove the thread identifier from the task's set of threads. + * 8) remove the thread object from the set of threads. + */ + +t_error thread_release(i_thread threadid) +{ + o_task* task; + o_thread* o; + + /* + * 1) + */ + + if (machine_call(thread, release, threadid) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 2) + */ + + if (thread_get(threadid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread object"); + + /* + * 3) + */ + + if (task_get(o->task, &task) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the task object"); + + /* + * 4) + */ + + if (o->stack.base != THREAD_STACK_ADDRESS_NONE) + { + if (map_release(task->as, o->stack.base) != ERROR_OK) + CORE_ESCAPE("unable to release the map for the thread's stack"); + } + + /* + * 5) + */ + + if (o->timer != ID_UNUSED) + { + if (timer_release(o->timer) != ERROR_OK) + CORE_ESCAPE("unable to release the timer"); + } + + /* + * 6) + */ + + if (id_release(&_thread.id, o->id) != ERROR_OK) + CORE_ESCAPE("unable to release the thread identifier"); + + /* + * 7) + */ + + if (set_remove(task->threads, threadid) != ERROR_OK) + CORE_ESCAPE("unable to remove the thread from the task's set of threads"); + + /* + * 8) + */ + + if (set_remove(_thread.threads, threadid) != ERROR_OK) + CORE_ESCAPE("unable to remove the thread identifier from the set of " + "threads"); + + CORE_LEAVE(); +} + +/* + * this function updates the thread's priority. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the thread and task objects. + * 2) if the priority is identical, exit. + * 3) update the thread's priority. + * 4) call the machine. + * 5) finally, if both the task and thread are running, request the + * scheduler to re-compute the thred's scheduling priority and, if + * necessary, to move the thread to another queue etc. this operation + * is performed after calling the machine because the execution may + * be yielded. + */ + +t_error thread_priority(i_thread threadid, + t_priority prior) +{ + o_task* task; + o_thread* o; + + /* + * 0) + */ + + if ((prior < THREAD_PRIORITY_LOW) || + (prior > THREAD_PRIORITY_HIGH)) + CORE_ESCAPE("invalid priority"); + + /* + * 1) + */ + + if (thread_get(threadid, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread object"); + + if (task_get(o->task, &task) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + if (o->priority == prior) + CORE_LEAVE(); + + /* + * 3) + */ + + o->priority = prior; + + /* + * 4) + */ + + if (machine_call(thread, priority, threadid, prior) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 5) + */ + + if ((task->state == TASK_STATE_START) && + (o->state == THREAD_STATE_START)) + { + if (scheduler_update(threadid) != ERROR_OK) + CORE_ESCAPE("unable to update the scheduled thread"); + } + + CORE_LEAVE(); +} + +/* + * this function starts a thread. + * + * steps: + * + * 1) retrieve the thread and task objects. + * 2) if the thread is already running, return an error. + * 3) modify the thread's state. + * 4) go through the waiting thread in order to wake up the ones waiting + * for this change. + * a) retrieve the thread identifier and object. + * b) if the thread is waiting for the thread to start. + * i) fill the waiting thread's wait structure with the cause: start. + * ii) wake up the waiting thread. + * iii) delete the waiting thread from the list. + * iv) restart the process since the set has been modified i.e the + * iterators may be inconsistent. + * 5) call the machine. + * 6) if the task is running, add the now running thread to the scheduler. + */ + +t_error thread_start(i_thread id) +{ + o_thread* thread; + o_task* task; + t_state s; + s_iterator i; + + /* + * 1) + */ + + if (thread_get(id, &thread) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread object"); + + if (task_get(thread->task, &task) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + if (thread->state == THREAD_STATE_START) + CORE_ESCAPE("a thread cannot be started twice"); + + /* + * 3) + */ + + thread->state = THREAD_STATE_START; + + /* + * 4) + */ + + try: + set_foreach(SET_OPTION_FORWARD, thread->waits, &i, s) + { + i_thread* id; + o_thread* o; + + /* + * a) + */ + + if (set_object(thread->waits, i, (void**)&id) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the waiting thread's identifier"); + + if (thread_get(*id, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the waiting thread"); + + /* + * b) + */ + + if (o->wait.state & WAIT_STATE_START) + { + /* + * i) + */ + + o->wait.cause = WAIT_STATE_START; + o->wait.value = WAIT_VALUE_UNKNOWN; + + /* + * ii) + */ + + if (thread_start(o->id) != ERROR_OK) + CORE_ESCAPE("unable to start the waiting thread"); + + /* + * iii) + */ + + if (set_delete(thread->waits, i) != ERROR_OK) + CORE_ESCAPE("unable to delete the thread from the waiting list"); + + /* + * iv) + */ + + goto try; + } + } + + /* + * 5) + */ + + if (machine_call(thread, start, id) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 6) + */ + + if (task->state == TASK_STATE_START) + { + if (scheduler_add(id) != ERROR_OK) + CORE_ESCAPE("unable to add the thread to the scheduler"); + } + + CORE_LEAVE(); +} + +/* + * this function stops a thread. + * + * steps: + * + * 1) retrieve the thread and task objects. + * 2) save the current thread's state. + * 3) if the thread is already stop, return an error. + * 4) modify the thread's state. + * 5) go through the set of waiting threads. + * a) retrieve the thread identifier and object. + * b) if the thread is waiting for the thread to stop. + * i) fill the waiting thread's wait structure with the cause: stop. + * ii) wake up the waiting thread. + * iii) delete the waiting thread from the list. + * iv) restart the process since the set has been modified i.e the + * iterators may be inconsistent. + * 5) call the machine. + * 6) if the task is running and the thread was previously running, + * remove the thread from the scheduler + */ + +t_error thread_stop(i_thread id) +{ + o_thread* thread; + o_task* task; + t_state state; + t_state s; + s_iterator i; + + /* + * 1) + */ + + if (thread_get(id, &thread) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread object"); + + if (task_get(thread->task, &task) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + state = thread->state; + + /* + * 3) + */ + + if (thread->state == THREAD_STATE_STOP) + CORE_ESCAPE("a thread cannot be stopped twice"); + + /* + * 4) + */ + + thread->state = THREAD_STATE_STOP; + + /* + * 5) + */ + + try: + set_foreach(SET_OPTION_FORWARD, thread->waits, &i, s) + { + i_thread* ith; + o_thread* o; + + /* + * a) + */ + + if (set_object(thread->waits, i, (void**)&ith) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the waiting thread's identifier"); + + if (thread_get(*ith, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the waiting thread"); + + /* + * b) + */ + + if (o->wait.state & WAIT_STATE_STOP) + { + /* + * i) + */ + + o->wait.cause = WAIT_STATE_STOP; + o->wait.value = WAIT_VALUE_UNKNOWN; + + /* + * ii) + */ + + if (thread_start(o->id) != ERROR_OK) + CORE_ESCAPE("unable to start the waiting thread"); + + /* + * iii) + */ + + if (set_delete(thread->waits, i) != ERROR_OK) + CORE_ESCAPE("unable to delete the thread from the waiting list"); + + /* + * iv) + */ + + goto try; + } + } + + /* + * 6) + */ + + if (machine_call(thread, stop, id) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 7) + */ + + if ((task->state == TASK_STATE_START) && + (state == THREAD_STATE_START)) + { + if (scheduler_remove(id) != ERROR_OK) + CORE_ESCAPE("unable to remove the thread from the scheduler"); + } + + CORE_LEAVE(); +} + +/* + * this function blocks a thread. + * + * steps: + * + * 1) retrieve the thread and task objects. + * 2) save the thread's current state. + * 3) if the thread is already blocked, return an error. + * 4) modify the thread's state. + * 5) call the machine. + * 6) if the task is running and the thread was previously running, + * remove the thread from the scheduler. + */ + +t_error thread_block(i_thread id) +{ + o_task* task; + o_thread* thread; + t_state state; + + /* + * 1) + */ + + if (thread_get(id, &thread) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread object"); + + if (task_get(thread->task, &task) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + state = thread->state; + + /* + * 3) + */ + + if (thread->state == THREAD_STATE_BLOCK) + CORE_ESCAPE("a thread cannot be blocked twice"); + + /* + * 4) + */ + + thread->state = THREAD_STATE_BLOCK; + + /* + * 5) + */ + + if (machine_call(thread, block, id) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 6) + */ + + if ((task->state == TASK_STATE_START) && + (state == THREAD_STATE_START)) + { + if (scheduler_remove(id) != ERROR_OK) + CORE_ESCAPE("unable to remove the thread from the scheduler"); + } + + CORE_LEAVE(); +} + +/* + * this function exits a thread, passing the exit value to the threads + * waiting for this event. + * + * note that this function performs a quite unusual operation. the morgue, + * in which the dead thread are stored, relies on a timer. unfortunately, + * the timer manager must be initialized after the thread manager. therefore + * the thread manager could not reserve a timer in its initialization phase. + * the timer reservation has thus been put in this function such that, if + * no task dies, the timer is never reserved and the morgue never run. + * + * steps: + * + * ~) if required, reserve the timer associated with the morgue. + * 1) retrieve the thread and task objects. + * 2) save the thread's current state. + * 3) if the thread is already dying or dead, return an error. + * 4) store the exit value in the thread object. + * 5) change the thread's state. + * 6) go through the waiting threads. + * a) retrieve the thread identifier. + * b) retrieve the thread object. + * c) if the thread is waiting for the exiting thread to exit. + * i) set the waiting state by specific both the cause, i.e death, and + * the exit value. + * ii) wake up the thread. + * iii) delete the thread from the waiting list. + * iv) mark the fact that someone took notice of the thread's death. + * v) restart the whole process since the set has been modified, the + * iterator may be inconsistent. + * 7) if someone took notice of the thread's death, the thread can be buried. + * a) change the thread's state to dead. + * b) add the thread identifer to the morgue. + * 8) call the machine. + * 9) if the task is running and the thread was running, remove the thread + * from the scheduler. + */ + +t_error thread_exit(i_thread id, + t_value value) +{ + o_task* task; + o_thread* object; + t_state state; + s_iterator i; + t_state s; + t_boolean w; + + /* + * ~) + */ + + if (_thread.morgue.timer == ID_UNUSED) + { + if (timer_reserve(TIMER_TYPE_FUNCTION, + TIMER_ROUTINE(thread_bury), + TIMER_DATA(NULL), + THREAD_MORGUE_DELAY, + TIMER_OPTION_REPEAT, + &_thread.morgue.timer) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve the timer"); + } + + /* + * 1) + */ + + if (thread_get(id, &object) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread object"); + + if (task_get(object->task, &task) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + state = object->state; + + /* + * 3) + */ + + if ((object->state == THREAD_STATE_ZOMBIE) || + (object->state == THREAD_STATE_DEAD)) + CORE_ESCAPE("a thread cannot be exited twice"); + + /* + * 4) + */ + + object->value = value; + + /* + * 5) + */ + + object->state = THREAD_STATE_ZOMBIE; + + /* + * 6) + */ + + w = BOOLEAN_FALSE; + + try: + set_foreach(SET_OPTION_FORWARD, object->waits, &i, s) + { + i_thread* id; + o_thread* o; + + /* + * a) + */ + + if (set_object(object->waits, i, (void**)&id) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the waiting thread's identifier"); + + /* + * b) + */ + + if (thread_get(*id, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the waiting thread"); + + /* + * c) + */ + + if (o->wait.state & WAIT_STATE_DEATH) + { + /* + * i) + */ + + o->wait.cause = WAIT_STATE_DEATH; + o->wait.value = object->value; + + /* + * ii) + */ + + if (thread_start(o->id) != ERROR_OK) + CORE_ESCAPE("unable to start the waiting thread"); + + /* + * iii) + */ + + if (set_delete(object->waits, i) != ERROR_OK) + CORE_ESCAPE("unable to delete the thread from the waiting list"); + + /* + * iv) + */ + + w = BOOLEAN_TRUE; + + /* + * v) + */ + + goto try; + } + } + + /* + * 7) + */ + + if (w == BOOLEAN_TRUE) + { + /* + * a) + */ + + object->state = THREAD_STATE_DEAD; + + /* + * b) + */ + + if (set_add(_thread.morgue.field, &object->id) != ERROR_OK) + CORE_ESCAPE("unable to add the thread to the morgue"); + } + + /* + * 8) + */ + + if (machine_call(thread, exit, id, value) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 9) + */ + + if ((task->state == TASK_STATE_START) && + (state == THREAD_STATE_START)) + { + if (scheduler_remove(id) != ERROR_OK) + CORE_ESCAPE("unable to remove the thread from the scheduler"); + } + + CORE_LEAVE(); +} + +/* + * this function is triggered on a regular basis in order to bury the dead + * threads. + * + * steps: + * + * 1) go through the dead threads. + * a) retrieve the thread identifier and object along with the task + * object. + * b) if the thread is not dead, it should not be here. this is a fatal + * error. + * c) save the thread's exit value. + * d) release the thread. + * e) remove the thread identifier from the morgue. + * f) if there are no more threads in the task which is neither a zombie + * nor dead, exit the task with the exit value used for exiting the + * last thread. + */ + +void thread_bury(i_timer timer, + t_vaddr data) +{ + s_iterator i; + + /* + * 1) + */ + + while (set_head(_thread.morgue.field, &i) == ERROR_TRUE) + { + i_thread* id; + o_thread* thread; + o_task* task; + t_value value; + + /* + * a) + */ + + assert(set_object(_thread.morgue.field, i, (void**)&id) == ERROR_OK); + + assert(thread_get(*id, &thread) == ERROR_OK); + + assert(task_get(thread->task, &task) == ERROR_OK); + + /* + * b) + */ + + assert(thread->state == THREAD_STATE_DEAD); + + /* + * c) + */ + + value = thread->value; + + /* + * d) + */ + + assert(thread_release(thread->id) == ERROR_OK); + + /* + * e) + */ + + assert(set_delete(_thread.morgue.field, i) == ERROR_OK); + + /* + * f) + */ + + if ((task->state != TASK_STATE_ZOMBIE) && + (task->state != TASK_STATE_DEAD) && + (set_empty(task->threads) == ERROR_TRUE)) + assert(task_exit(task->id, value) == ERROR_OK); + } +} + +/* + * this function waits for a thread to change state. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the thread and target task objects. + * 2) check that the thread willing to wait is running. + * 3) if the thread is waiting for the thread to start and the thread is + * already running, fill and return the information. + * 4) if the thread is waiting for the thread to stop and the thread is + * already stopped, fill and return the information. + * 5) if the thread is waiting for the thread to exit and the thread is + * already dying, fill and return the information. + * a) fill the waiting thread's wait structure. + * b) set the thread as being dead now that someone has taken notice + * of its death. + * c) add the thread to the morgue. + * 6) if the thread is already dead, there is nothing to wait for, return an + * error as it is too late. + * 7) set the thread's waiting state as the one provided. it is necessary + * to store such an information for later when the thread will finally + * change its state. at that moment, the task will have to know what + * the thread is waiting for to decide wheter to wake him up or not. + * 8) add the thread to the thread's waiting list. + * 9) stop the thread. the thread will be woken up by the thread once it will + * have changed to the given state. therefore the next steps are executed + * only after the thread has been woken up. + * 10) retrieve the information passed by the thread on the event that led + * to the thread being woken up i.e the cause but also the value should + * the task have exited. note that these information are passed by the + * thread through the waiting thread's object 'wait' specific attribute. + * 11) re-initialize the thread has being waiting for nothing. + * 12) call the machine. + */ + +t_error thread_wait(i_thread id, + i_thread target, + t_state state, + s_wait* wait) +{ + o_thread* object; + o_thread* o; + + /* + * 0) + */ + + if (wait == NULL) + CORE_ESCAPE("the 'wait' argument is null"); + + /* + * 1) + */ + + if (thread_get(id, &object) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread object"); + + if (thread_get(target, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread object"); + + /* + * 2) + */ + + if (object->state != THREAD_STATE_START) + CORE_ESCAPE("unable to make a non-running thread wait for a thread"); + + /* + * 3) + */ + + if ((state & WAIT_STATE_START) && + (o->state == THREAD_STATE_START)) + { + wait->id.thread = target; + wait->state = state; + wait->cause = WAIT_STATE_START; + wait->value = WAIT_VALUE_UNKNOWN; + + CORE_LEAVE(); + } + + /* + * 4) + */ + + if ((state & WAIT_STATE_STOP) && + (o->state == THREAD_STATE_STOP)) + { + wait->id.thread = target; + wait->state = state; + wait->cause = WAIT_STATE_STOP; + wait->value = WAIT_VALUE_UNKNOWN; + + CORE_LEAVE(); + } + + /* + * 5) + */ + + if ((state & WAIT_STATE_DEATH) && + (o->state == THREAD_STATE_ZOMBIE)) + { + /* + * a) + */ + + wait->id.thread = target; + wait->state = state; + wait->cause = WAIT_STATE_DEATH; + wait->value = o->value; + + /* + * b) + */ + + o->state = THREAD_STATE_DEAD; + + /* + * c) + */ + + if (set_add(_thread.morgue.field, &o->id) != ERROR_OK) + CORE_ESCAPE("unable to add the thread to the morgue"); + + CORE_LEAVE(); + } + + /* + * 6) + */ + + if (o->state == THREAD_STATE_DEAD) + CORE_ESCAPE("unable to wait for a dead thread"); + + /* + * 7) + */ + + object->wait.state = state; + + /* + * 8) + */ + + if (set_add(o->waits, &id) != ERROR_OK) + CORE_ESCAPE("unable to add the thread identifier to the waiting list"); + + /* + * 9) + */ + + if (thread_stop(id) != ERROR_OK) + CORE_ESCAPE("unable to stop the task"); + + /* + * 10) + */ + + wait->id.thread = target; + wait->state = object->wait.state; + wait->cause = object->wait.cause; + wait->value = object->wait.value; + + /* + * 11) + */ + + object->wait.state = WAIT_STATE_NONE; + + /* + * 12) + */ + + if (machine_call(thread, wait, id, target, state, wait) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function pushes arguments on the thread's stack so that, when + * the thread starts, it can retrieve the arguments in its address space. + * + * note that this operation is left to the machine to implement since + * highly machine-specific. + */ + +t_error thread_arguments(i_thread threadid, + void* arguments, + t_vsize size) +{ + if (machine_call(thread, arguments, threadid, arguments, size) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function is called when a thread must be woken up. + * + * steps: + * + * 1) retrieve the thread object. + * 2) re-initialize the thread's timer. + * 3) wake up the thread. + */ + +void thread_wakeup(i_timer timer, + t_vaddr data) +{ + i_thread* id = (i_thread*)data; + o_thread* o; + + /* + * 1) + */ + + assert(thread_get(*id, &o) == ERROR_OK); + + /* + * 2) + */ + + o->timer = ID_UNUSED; + + /* + * 3) + */ + + assert(thread_start(o->id) == ERROR_OK); +} + +/* + * this function puts the given thread to sleep. + * + * note that every thread object embeds an available timer identifier which, + * in this case, is used to trigger the wakeup function. + * + * another particularity comes from the fact that the id of the thread to + * wake up is passed as the data to the timer. note that since the object + * is not supposed to be released before the timer is triggered, the + * address of the identifier within the thread object is actually passed. + * + * steps: + * + * 1) retrieve the thread object. + * 2) if the thread is not running, it cannot be put to sleep. + * 3) reserve a timer. + * 4) call the machine. + * 5) finally, block the thread. + */ + +t_error thread_sleep(i_thread id, + t_delay milliseconds) +{ + o_thread* o; + + /* + * 1) + */ + + if (thread_get(id, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread object"); + + /* + * 2) + */ + + if (o->state != THREAD_STATE_START) + CORE_ESCAPE("unable to put to sleep a non-running thread"); + + /* + * 3) + */ + + if (timer_reserve(TIMER_TYPE_FUNCTION, + TIMER_ROUTINE(thread_wakeup), + TIMER_DATA(&o->id), + milliseconds, + TIMER_OPTION_NONE, + &o->timer) != ERROR_OK) + CORE_ESCAPE("unable to reserve a timer"); + + /* + * 4) + */ + + if (machine_call(thread, sleep, id, milliseconds) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 5) + */ + + if (thread_block(o->id) != ERROR_OK) + CORE_ESCAPE("unable to block the thread"); + + CORE_LEAVE(); +} + +/* + * this function removes every thread that belongs to the specified task. + * + * steps: + * + * 1) call the machine. + * 2) retrieve the task object. + * 3) go through the threads. + * a) retrieve the thread identifier. + * b) release the thread. + */ + +t_error thread_flush(i_task taskid) +{ + o_task* task; + s_iterator i; + + /* + * 1) + */ + + if (machine_call(thread, flush, taskid) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 2) + */ + + if (task_get(taskid, &task) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the task object"); + + /* + * 3) + */ + + while (set_head(task->threads, &i) == ERROR_TRUE) + { + i_thread* id; + + /* + * a) + */ + + if (set_object(task->threads, i, (void**)&id) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread identifier"); + + /* + * b) + */ + + if (thread_release(*id) != ERROR_OK) + CORE_ESCAPE("unable to release the thread"); + } + + CORE_LEAVE(); +} + +/* + * this function loads an execution context in the thread. + * + * note that since this operation is machine-specific, the implementation + * is left to the machine. + */ + +t_error thread_load(i_thread threadid, + s_thread_context context) +{ + if (machine_call(thread, load, threadid, context) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this fonction stores the thread execution context in the given context. + * + * note that since this operation is machine-specific, the implementation + * is left to the machine. + * + * steps: + * + * 0) verify the arguments. + * 1) call the machine. + */ + +t_error thread_store(i_thread threadid, + s_thread_context* context) +{ + /* + * 0) + */ + + if (context == NULL) + CORE_ESCAPE("the 'context' argument is null"); + + /* + * 1) + */ + + if (machine_call(thread, store, threadid, context) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function returns the identifier of the thread being currently + * executed. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the scheduler associated with the current CPU. + * 2) return the identifier of the thread being currently scheduled. + */ + +t_error thread_current(i_thread* thread) +{ + o_scheduler* scheduler; + + /* + * 0) + */ + + if (thread == NULL) + CORE_ESCAPE("the 'thread' argument is null"); + + /* + * 1) + */ + + if (scheduler_current(&scheduler) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the current scheduler"); + + /* + * 2) + */ + + *thread = scheduler->thread; + + CORE_LEAVE(); +} + +/* + * this function returns true if the given thread object exists. + */ + +t_error thread_exist(i_thread threadid) +{ + if (set_exist(_thread.threads, threadid) != ERROR_TRUE) + CORE_FALSE(); + + CORE_TRUE(); +} + +/* + * this function retrieves the thread object identified by the given + * identifier. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the object from the set. + */ + +t_error thread_get(i_thread id, + o_thread** object) +{ + /* + * 0) + */ + + if (object == NULL) + CORE_ESCAPE("the 'object' argument is null"); + + /* + * 1) + */ + + if (set_get(_thread.threads, id, (void**)object) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the object from the set of threads"); + + CORE_LEAVE(); +} + +/* + * this function initializes the thread manager. + * + * steps: + * + * 1) display a message. + * 2) initialize the thread manager's structure. + * 3) build the identifier object. + * 4) reserve the set of threads. + * 5) reserve the set of dead threads i.e the morgue. + * 6) initialize the morgue's timer as being unused. this is because, at + * that time, timers cannot be reserved because the timer manager has + * not been set up yet. + * 7) reserve the kernel thread though this thread will never be scheduled + * per say. + * 8) block the thread: this is to make things clear that this thread + * is not supposed to be scheduled. + * 9) call the machine. + */ + +t_error thread_initialize(void) +{ + /* + * 1) + */ + + module_call(console, message, + '+', "initializing the thread manager\n"); + + /* + * 2) + */ + + memset(&_thread, 0x0, sizeof (m_thread)); + + /* + * 3) + */ + + if (id_build(&_thread.id) != ERROR_OK) + CORE_ESCAPE("unable to build the identifier object"); + + /* + * 4) + */ + + if (set_reserve(ll, + SET_OPTION_ALLOCATE, + sizeof (o_thread), + &_thread.threads) != ERROR_OK) + CORE_ESCAPE("unable to reserve the set of threads"); + + /* + * 5) + */ + + if (set_reserve(ll, + SET_OPTION_ALLOCATE | SET_OPTION_SORT, + sizeof (i_thread), + &_thread.morgue.field) != ERROR_OK) + CORE_ESCAPE("unable to reserve the set of dead threads i.e the morgue"); + + /* + * 6) + */ + + _thread.morgue.timer = ID_UNUSED; + + /* + * 7) + */ + + if (thread_reserve(_kernel.task, + THREAD_PRIORITY, + (t_vaddr)_init->kstack, + (t_vsize)_init->kstacksz, + (t_vaddr)NULL, + &_kernel.thread) != ERROR_OK) + CORE_ESCAPE("unable to reserve the kernel thread"); + + /* + * 8) + */ + + if (thread_block(_kernel.thread) != ERROR_OK) + CORE_ESCAPE("unable to block the kernel thread"); + + /* + * 9) + */ + + if (machine_call(thread, initialize) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * destroy the thread manager. + * + * steps: + * + * 1) display a message. + * 2) call the machine. + * 3) go through the threads. + * a) retrieve the thread identifier. + * b) release the thread. + * 4) if required, release the morgue's timer. + * 5) release the set of dead threads. + * 6) release the set of threads. + * 7) destroy the identifier object. + */ + +t_error thread_clean(void) +{ + s_iterator i; + t_state s; + + /* + * 1) + */ + + module_call(console, message, + '+', "cleaning the thread manager\n"); + + /* + * 2) + */ + + if (machine_call(thread, clean) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 3) + */ + + set_foreach(SET_OPTION_FORWARD, _thread.threads, &i, s) + { + i_thread* thread; + + /* + * a) + */ + + if (set_object(_thread.threads, i, (void**)&thread) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the thread identifier"); + + /* + * b) + */ + + if (thread_release(*thread) != ERROR_OK) + CORE_ESCAPE("unable to release the thread"); + } + + /* + * 4) + */ + + if (_thread.morgue.timer != ID_UNUSED) + { + if (timer_release(_thread.morgue.timer) != ERROR_OK) + CORE_ESCAPE("unable to release the timer"); + } + + /* + * 5) + */ + + if (set_release(_thread.morgue.field) != ERROR_OK) + CORE_ESCAPE("unable to release the set of dead threads"); + + /* + * 6) + */ + + if (set_release(_thread.threads) != ERROR_OK) + CORE_ESCAPE("unable to release the set of threads"); + + /* + * 7) + */ + + if (id_destroy(&_thread.id) != ERROR_OK) + CORE_ESCAPE("unable to destroy the identifier object"); + + CORE_LEAVE(); +} diff --git a/kaneton/core/timer/Makefile b/kaneton/core/timer/Makefile new file mode 100644 index 0000000..8fd5c83 --- /dev/null +++ b/kaneton/core/timer/Makefile @@ -0,0 +1,76 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/kaneton/core/timer/Makefile +# +# created julien quintard [sun jun 10 17:11:24 2007] +# updated julien quintard [fri dec 10 15:27:32 2010] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/core/timer + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +TIMER_C := timer.c + +TIMER_O := $(TIMER_C:.c=.o) + +TIMER_INCLUDE := $(_CORE_INCLUDE_DIR_)/timer.h + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(_TIMER_LO_) + +$(_TIMER_LO_): $(TIMER_O) + $(call env_remove,$(_TIMER_LO_),) + + $(call env_archive,$(_TIMER_LO_),$(TIMER_O),) + +clear: + $(call env_remove,$(TIMER_O),) + + $(call env_remove,$(_TIMER_LO_),) + + $(call env_purge,) + +prototypes: + $(call env_prototypes,$(TIMER_INCLUDE),) + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(TIMER_C),) + +dependencies: + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/core/timer/timer.c b/kaneton/core/timer/timer.c new file mode 100644 index 0000000..2de58de --- /dev/null +++ b/kaneton/core/timer/timer.c @@ -0,0 +1,1207 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/core/timer/timer.c + * + * created renaud voltz [sun feb 12 23:04:54 2006] + * updated julien quintard [wed mar 2 20:27:23 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * the timer manager provides functionalities for triggering functions, or + * sending message notifications, after some time has passed. + * + * note that timer objects are kept in an ordered list, with the + * about-to-be-triggered timers first. therefore, whenever the manager + * wants to check whether some timers have expired, it just checks if + * the object's delay has expired. if so the timer's expiration is notified + * while the next timer is checked until a timer is detected as not having + * expired in which case, since the timers are ordered according to their + * delay, all the following timers do not need to be checked. + * + * also, the timer's delay are relative to each other. for instance considering + * the following set of delays (15, 20, 28, 38, 40, 40 and 100) would be + * stored in the sorted set as follows: + * + * 15, 5, 8, 10, 2, 0, 60 + * + * note that the first timer keeps its absolute value while the others + * are relative to the previous one. + * + * since timers can have the same delay, mapping the timer identifier on + * the delay would incurr identifier collisions. an identifier is thus + * generated. this implies that the sorting cannot be left to the set + * manager since the identifiers sorting does not reflect the timers + * sorting. therefore, the manager itself handles both the objects' memory + * and their placement in the list. + * + * in addition, note that a timer can be either internal or external. the + * first type is associated with an internal kernel function which is called + * on expiration while the second generates a message which is sent to the + * destination task. + * + * finally, timers can be configured, through the REPEAT option, in order + * to be triggered on a regular basis, every 'delay' milliseconds. therefore, + * once such a timer has expired, its delay is re-initialised to its original + * value and the timer is re-located in the list, according to its new delay. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * include the machine-specific definitions required by the core. + */ + +machine_include(timer); + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the kernel manager. + */ + +extern m_kernel _kernel; + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the timer manager. + */ + +m_timer _timer; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function is triggered by the hardware timer event on a regular + * basis. + * + * note that since the hardware timer event is, obviously, machine-dependent, + * it is the machine's reponsability to reserve this event so that this + * function gets triggered. + * + * steps: + * + * 1) update the clock. + * 2) check the timers' expiration. + */ + +void timer_handler(i_event event, + t_data data) +{ + /* + * 1) + */ + + assert(clock_update(TIMER_DELAY) == ERROR_OK); + + /* + * 2) + */ + + assert(timer_check() == ERROR_OK); +} + +/* + * this function is called on a regular basis and check whether the + * timers have expired or not. if so, the expiration is notified to its + * associated target. + * + * steps: + * + * 1) go through the timers. + * a) retrieve the timer object. + * b) if the timer still needs to wait to be triggered, decrease it by + * the amount of time that has passed and exit the loop since, if + * this timer has not expired, none of the following has. + * c) since this timer has expired, substract its delay to the amount + * of time that needs to be substracted from all the expiring timers. + * d) update the timer's delay to zero since it has expired. + * e) notify of the timer's expiration. + * f) if the timer must be repeated, update it so that it gets re-inserted + * in the list. otherwise, release the timer since no longer used. + * g) finally move on to the next timer which may also have expired. + */ + +t_error timer_check(void) +{ + t_delay reference = TIMER_DELAY; + s_iterator i; + + /* + * 1) + */ + + while (set_head(_timer.timers, &i) == ERROR_TRUE) + { + o_timer* o; + + /* + * a) + */ + + if (set_object(_timer.timers, i, (void**)&o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the timer"); + + /* + * b) + */ + + if (o->delay > reference) + { + o->delay -= reference; + + break; + } + + /* + * c) + */ + + reference -= o->delay; + + /* + * d) + */ + + o->delay = 0; + + /* + * e) + */ + + if (timer_notify(o->id) != ERROR_OK) + CORE_ESCAPE("unable to notify of the timer's expiration"); + + /* + * f) + */ + + if (o->options & TIMER_OPTION_REPEAT) + { + if (timer_update(o->id, o->repeat) != ERROR_OK) + CORE_ESCAPE("unable to update the timer"); + } + else + { + if (timer_release(o->id) != ERROR_OK) + CORE_ESCAPE("unable to release the timer"); + } + + /* + * g) + */ + } + + CORE_LEAVE(); +} + +/* + * this function shows a timer's attributes. + * + * steps: + * + * 1) retrieve the timer object. + * 2) build the options string. + * 3) depending on the timer type, display its attributes. + * 4) call the machine. + */ + +t_error timer_show(i_timer id, + mt_margin margin) +{ + o_timer* o; + char options[2]; + + /* + * 1) + */ + + if (timer_get(id, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the timer object"); + + /* + * 2) + */ + + if (o->options & TIMER_OPTION_REPEAT) + options[0] = 'r'; + else + options[0] = '.'; + + options[1] = '\0'; + + /* + * 3) + */ + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + "timer:\n", + MODULE_CONSOLE_MARGIN_VALUE(margin)); + + switch (o->type) + { + case TIMER_TYPE_FUNCTION: + { + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " core: id(%qd) type(function) delay(%qd) options(%s) " + "repeat(%qd) routine(0x%x) data(0x%x)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + o->id, + o->delay, + options, + o->repeat, + o->handler.routine, + o->data); + + break; + } + case TIMER_TYPE_MESSAGE: + { + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " core: id(%qd) type(message) delay(%qd) options(%s) " + "repeat(%qd) task(%qd) data(0x%x)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + o->id, + o->delay, + options, + o->repeat, + o->handler.task, + o->data); + + break; + } + default: + CORE_ESCAPE("unknown timer type '%u'", + o->type); + } + + /* + * 4) + */ + + if (machine_call(timer, show, id, margin) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function dumps the timer manager. + * + * steps: + * + * 1) display general information on the manager. + * 2) show the identifier object. + * 3) retrieve the size of the set of timers. + * 4) display the timers. + * a) retrieve the timer object. + * b) show the timer. + * 5) call the machine. + */ + +t_error timer_dump(void) +{ + t_setsz size; + s_iterator i; + t_state s; + + /* + * 1) + */ + + module_call(console, message, + '#', "timer manager:\n"); + + module_call(console, message, + '#', " core: timers(%qd)\n", + _timer.timers); + + /* + * 2) + */ + + if (id_show(&_timer.id, + 2 * MODULE_CONSOLE_MARGIN_SHIFT) != ERROR_OK) + CORE_ESCAPE("unable to show the identifier object"); + + /* + * 3) + */ + + if (set_size(_timer.timers, &size) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the size of the set of timers"); + + /* + * 4) + */ + + module_call(console, message, + '#', " timers: id(%qd) size(%qd)\n", + _timer.timers, + size); + + set_foreach(SET_OPTION_FORWARD, _timer.timers, &i, s) + { + o_timer* o; + + /* + * a) + */ + + if (set_object(_timer.timers, i, (void**)&o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the timer"); + + /* + * b) + */ + + if (timer_show(o->id, + 3 * MODULE_CONSOLE_MARGIN_SHIFT) != ERROR_OK) + CORE_ESCAPE("unable to show the timer"); + } + + /* + * 5) + */ + + if (machine_call(timer, dump) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function notifies the destination, i.e depending on the timer type, + * of the timer's expiration. + * + * steps: + * + * 1) retrieve the timer object. + * 2) depending on the timer type. + * A) if the timer target is a function... + * a) call the associated routine. + * B) if the timer target is a message... + * a) build the destination node. + * b) build the notification message. + * c) send the message. + * 3) call the machine. + */ + +t_error timer_notify(i_timer id) +{ + o_timer* o; + + /* + * 1) + */ + + if (timer_get(id, &o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the timer object"); + + /* + * 2) + */ + + switch (o->type) + { + case TIMER_TYPE_FUNCTION: + { + /* + * A) + */ + + /* + * a) + */ + + o->handler.routine(o->id, o->data); + + break; + } + case TIMER_TYPE_MESSAGE: + { + /* + * B) + */ + + o_timer_message message; + i_node node; + + /* + * a) + */ + + node.cell = _kernel.cell; + node.task = o->handler.task; + + /* + * b) + */ + + message.id = id; + message.data = o->data; + + /* + * c) + */ + + if (message_send(_kernel.task, + node, + MESSAGE_TYPE_TIMER, + (t_vaddr)&message, + sizeof (o_timer_message)) != ERROR_OK) + CORE_ESCAPE("unable to send a message to the task"); + } + default: + CORE_ESCAPE("unknown timer type '%u'", + o->type); + } + + /* + * 3) + */ + + if (machine_call(timer, notify, id) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function reserves a timer. + * + * steps: + * + * 0) verify the arguments. + * 1) reserve a timer identifier. + * 2) allocate memory for the timer object. + * 3) initialize and fill the object. + * 4) if the repeat option has been activated, save the original delay. + * 5) insert the object in the set, according to its state: + * A) if the set is empty... + * a) since this is the only timer, set the relative delay to the + * absolute's size. + * b) add the timer. + * B) if there already is a timer, or more... + * a) initialize the absolute reference delay. + * b) go through the timers. + * i) retrieve the timer object. + * ii) add the timer's relative delay to the absolute delay. + * iii) if the absolute delay of the reserved timer is larger than + * the absolute delay of the timer being handled in the loop, this + * means that the timer must be stored further in the set; continue + * with the next timer. + * iv) compute the reserved timer's delay depending on its position i.e + * first or following another timer. + * #1) if the timer is the first one, the object's relative delay + * equals the absolute one. + * #2) if the timer is preceded by another time, the absolute value + * given through timer_reserve() must be converted in a delay + * relative to the previous timer's: difference between the + * absolute delays of the reserved and previous timers. + * v) update the current timer since its relative delay must be adjusted + * to the reserved timer which will soon precede it. + * vi) insert the new timer before the current one. + * vii) the timer has been inserted properly, exit the loop. + * c) if the function reaches this point, no timer with a higher delay + * was found. therefore, set the object's relative delay as being + * the absolute provided through timer_reserve() minus the sum + * of all the timers i.e _reference_. + * d) add the timer after the last timer i.e at the end. + * 6) call the machine. + */ + +t_error timer_reserve(t_type type, + u_timer_handler handler, + t_data data, + t_delay delay, + t_options options, + i_timer* id) +{ + t_delay reference; + o_timer* o; + s_iterator i; + t_state s; + + /* + * 0) + */ + + if ((type != TIMER_TYPE_FUNCTION) && + (type != TIMER_TYPE_MESSAGE)) + CORE_ESCAPE("invalid type"); + + if (id == NULL) + CORE_ESCAPE("the 'id' argument is null"); + + /* + * 1) + */ + + if (id_reserve(&_timer.id, id) != ERROR_OK) + CORE_ESCAPE("unable to reserve a timer identifier"); + + /* + * 2) + */ + + if ((o = malloc(sizeof (o_timer))) == NULL) + CORE_ESCAPE("unable to allocate memory for the timer object"); + + /* + * 3) + */ + + memset(o, 0x0, sizeof (o_timer)); + + o->id = *id; + o->type = type; + o->handler = handler; + o->data = data; + o->options = options; + + /* + * 4) + */ + + if (o->options & TIMER_OPTION_REPEAT) + o->repeat = delay; + + /* + * 5) + */ + + if (set_empty(_timer.timers) == ERROR_TRUE) + { + /* + * A) + */ + + /* + * a) + */ + + o->delay = delay; + + /* + * b) + */ + + if (set_add(_timer.timers, o) != ERROR_OK) + CORE_ESCAPE("unable to append the timer object at the end of the set"); + } + else + { + /* + * B) + */ + + /* + * a) + */ + + reference = 0; + + /* + * b) + */ + + set_foreach(SET_OPTION_FORWARD, _timer.timers, &i, s) + { + o_timer* n; + s_iterator j; + + /* + * i) + */ + + if (set_object(_timer.timers, i, (void**)&n) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the timer"); + + /* + * ii) + */ + + reference += n->delay; + + /* + * iii) + */ + + if (delay >= reference) + continue; + + /* + * iv) + */ + + if (set_previous(_timer.timers, i, &j) == ERROR_FALSE) + { + /* + * #1) + */ + + o->delay = delay; + } + else + { + /* + * #2) + */ + + o->delay = delay - (reference - n->delay); + } + + /* + * v) + */ + + n->delay = n->delay - o->delay; + + /* + * vi) + */ + + if (set_before(_timer.timers, i, o) != ERROR_OK) + CORE_ESCAPE("unable to insert the timer in the set"); + + /* + * vii) + */ + + goto inserted; + } + + /* + * c) + */ + + o->delay = delay - reference; + + /* + * d) + */ + + if (set_after(_timer.timers, i, o) != ERROR_OK) + CORE_ESCAPE("unable to insert the timer in the set"); + } + + inserted: + + /* + * 6) + */ + + if (machine_call(timer, reserve, + type, + handler, + data, + delay, + options, + id) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function releases a timer. + * + * steps: + * + * 1) call the machine. + * 2) release the timer identifier. + * 3) locate and retrieve the timer object. + * 4) update the following timer's relative delay. + * a) retrieve the next timer, if there is one. + * b) re-compute its delay since its previous timer just got released. + * 5) release the object's memory. + * 6) delete the timer from the list. + */ + +t_error timer_release(i_timer id) +{ + o_timer* o; + s_iterator i; + s_iterator j; + + /* + * 1) + */ + + if (machine_call(timer, release, id) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 2) + */ + + if (id_release(&_timer.id, id) != ERROR_OK) + CORE_ESCAPE("unable to release the timer identifier"); + + /* + * 3) + */ + + if (set_locate(_timer.timers, id, &i) != ERROR_OK) + CORE_ESCAPE("unable to locate the timer object"); + + if (set_object(_timer.timers, i, (void**)&o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the timer object"); + + /* + * 4) + */ + + if (set_next(_timer.timers, i, &j) == ERROR_TRUE) + { + o_timer* t; + + /* + * a) + */ + + if (set_object(_timer.timers, j, (void**)&t) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the timer object"); + + /* + * b) + */ + + t->delay += o->delay; + } + + /* + * 5) + */ + + free(o); + + /* + * 6) + */ + + if (set_delete(_timer.timers, i) != ERROR_OK) + CORE_ESCAPE("unable to delete the object from the set of timers"); + + CORE_LEAVE(); +} + +/* + * this function updates the timer's delay. + * + * note that should the timer be repeated, its repeat delay is also + * updated. + * + * steps: + * + * 1) locate and retrieve the timer object. + * 2) delete the object from the list. + * 3) if the timer is being repeated, update its repeat delay. + * 4) insert the object in the set, according to its state: + * A) if the set is empty... + * a) since this is the only timer, set the relative delay to the + * absolute's size. + * b) add the timer. + * B) if there already is a timer, or more... + * a) initialize the absolute reference delay. + * b) go through the timers. + * i) retrieve the timer object. + * ii) add the timer's relative delay to the absolute delay. + * iii) if the absolute delay of the updated timer is larger than + * the absolute delay of the timer being handled in the loop, this + * means that the timer must be stored further in the set; continue + * with the next timer. + * iv) compute the updated timer's delay depending on its position i.e + * first or following another timer. + * #1) if the timer is the first one, the object's relative delay + * equals the absolute one. + * #2) if the timer is preceded by another time, the absolute value + * given through timer_update() must be converted in a delay + * relative to the previous timer's: difference between the + * absolute delays of the updated and previous timers. + * v) update the current timer since its relative delay must be adjusted + * to the updated timer which will soon precede it. + * vi) insert the new timer before the current one. + * vii) the timer has been inserted properly, exit the loop. + * c) if the function reaches this point, no timer with a higher delay + * was found. therefore, set the object's relative delay as being + * the absolute provided through timer_update() minus the sum + * of all the timers i.e _reference_. + * d) add the timer after the last timer i.e at the end. + * 5) call the machine. + */ + +t_error timer_update(i_timer id, + t_delay delay) +{ + t_delay reference; + o_timer* o; + s_iterator i; + t_state s; + + /* + * 1) + */ + + if (set_locate(_timer.timers, id, &i) != ERROR_OK) + CORE_ESCAPE("unable to locate the timer object"); + + if (set_object(_timer.timers, i, (void**)&o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the timer object"); + + /* + * 2) + */ + + if (set_delete(_timer.timers, i) != ERROR_OK) + CORE_ESCAPE("unable to delete the object from the set of timers"); + + /* + * 3) + */ + + if (o->options & TIMER_OPTION_REPEAT) + o->repeat = delay; + + /* + * 4) + */ + + if (set_empty(_timer.timers) == ERROR_TRUE) + { + /* + * A) + */ + + /* + * a) + */ + + o->delay = delay; + + /* + * b) + */ + + if (set_add(_timer.timers, o) != ERROR_OK) + CORE_ESCAPE("unable to append the timer object at the end of the set"); + } + else + { + /* + * B) + */ + + /* + * a) + */ + + reference = 0; + + /* + * b) + */ + + set_foreach(SET_OPTION_FORWARD, _timer.timers, &i, s) + { + o_timer* n; + s_iterator j; + + /* + * i) + */ + + if (set_object(_timer.timers, i, (void**)&n) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the timer"); + + /* + * ii) + */ + + reference += n->delay; + + /* + * iii) + */ + + if (delay >= reference) + continue; + + /* + * iv) + */ + + if (set_previous(_timer.timers, i, &j) == ERROR_FALSE) + { + /* + * #1) + */ + + o->delay = delay; + } + else + { + /* + * #2) + */ + + o->delay = delay - (reference - n->delay); + } + + /* + * v) + */ + + n->delay = n->delay - o->delay; + + /* + * vi) + */ + + if (set_before(_timer.timers, i, o) != ERROR_OK) + CORE_ESCAPE("unable to insert the timer in the set"); + + /* + * vii) + */ + + goto inserted; + } + + /* + * c) + */ + + o->delay = delay - reference; + + /* + * d) + */ + + if (set_after(_timer.timers, i, o) != ERROR_OK) + CORE_ESCAPE("unable to insert the timer in the set"); + } + + inserted: + + /* + * 5) + */ + + if (machine_call(timer, update, id, delay) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function releases all the timers. + * + * steps: + * + * 1) go through the timers. + * a) retrieve the timer object. + * b) release the timer. + */ + +t_error timer_flush(void) +{ + s_iterator i; + t_state s; + + /* + * 1) + */ + + set_foreach(SET_OPTION_FORWARD, _timer.timers, &i, s) + { + o_timer* o; + + /* + * a) + */ + + if (set_object(_timer.timers, i, (void**)&o) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the timer object"); + + /* + * b) + */ + + if (timer_release(o->id) != ERROR_OK) + CORE_ESCAPE("unable to release the timer"); + } + + CORE_LEAVE(); +} + +/* + * this function returns true if the timer object exists. + */ + +t_error timer_exist(i_timer id) +{ + if (set_exist(_timer.timers, id) != ERROR_TRUE) + CORE_FALSE(); + + CORE_TRUE(); +} + +/* + * this function retrieves a timer object from the set of timers. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the object from the set. + */ + +t_error timer_get(i_timer id, + o_timer** object) +{ + /* + * 0) + */ + + if (object == NULL) + CORE_ESCAPE("the 'object' argument is null"); + + /* + * 1) + */ + + if (set_get(_timer.timers, id, (void**)object) != ERROR_OK) + CORE_ESCAPE("unable to retrieve the object from the set of timers"); + + CORE_LEAVE(); +} + +/* + * this function initializes the timer manager. + * + * steps: + * + * 1) display a message. + * 2) initialize the timer manager's structure. + * 3) initialize the object identifier. + * 4) reserve the set of timers. + * 5) call the machine. + */ + +t_error timer_initialize(void) +{ + /* + * 1) + */ + + module_call(console, message, + '+', "initializing the timer manager\n"); + + /* + * 2) + */ + + memset(&_timer, 0x0, sizeof (m_timer)); + + /* + * 3) + */ + + if (id_build(&_timer.id) != ERROR_OK) + CORE_ESCAPE("unable to build the identifier object"); + + /* + * 4) + */ + + if (set_reserve(ll, + SET_OPTION_NONE, + sizeof (o_timer), + &_timer.timers) != ERROR_OK) + CORE_ESCAPE("unable to reserve the set of timers"); + + /* + * 5) + */ + + if (machine_call(timer, initialize) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + CORE_LEAVE(); +} + +/* + * this function cleans the timer manager. + * + * steps: + * + * 1) display a message. + * 2) call the machine. + * 3) flush the timers. + * 3) release the set of timers. + * 5) destroy the identifier object. + */ + +t_error timer_clean(void) +{ + /* + * 1) + */ + + module_call(console, message, + '+', "cleaning the timer manager\n"); + + /* + * 2) + */ + + if (machine_call(timer, clean) != ERROR_OK) + CORE_ESCAPE("an error occured in the machine"); + + /* + * 3) + */ + + if (timer_flush() != ERROR_OK) + CORE_ESCAPE("unable to flush the timer"); + + /* + * 4) + */ + + if (set_release(_timer.timers) != ERROR_OK) + CORE_ESCAPE("unable to release the set of timers"); + + /* + * 5) + */ + + if (id_destroy(&_timer.id) != ERROR_OK) + CORE_ESCAPE("unable to destroy the identifier object"); + + CORE_LEAVE(); +} diff --git a/kaneton/include/architecture b/kaneton/include/architecture new file mode 120000 index 0000000..9bed329 --- /dev/null +++ b/kaneton/include/architecture @@ -0,0 +1 @@ +../machine/architecture/.current/include \ No newline at end of file diff --git a/kaneton/include/core b/kaneton/include/core new file mode 120000 index 0000000..b81fb0b --- /dev/null +++ b/kaneton/include/core @@ -0,0 +1 @@ +../core/include \ No newline at end of file diff --git a/kaneton/include/glue b/kaneton/include/glue new file mode 120000 index 0000000..94ac145 --- /dev/null +++ b/kaneton/include/glue @@ -0,0 +1 @@ +../machine/glue/.current/include \ No newline at end of file diff --git a/kaneton/include/kaneton.h b/kaneton/include/kaneton.h new file mode 100644 index 0000000..62d7d07 --- /dev/null +++ b/kaneton/include/kaneton.h @@ -0,0 +1,36 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/include/kaneton.h + * + * created julien quintard [thu jun 7 12:44:42 2007] + * updated julien quintard [fri dec 17 20:57:53 2010] + */ + +#ifndef KANETON_H +#define KANETON_H 1 + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * this macro indicates that the current kernel is kaneton. + */ + +#define ___kaneton + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include +#include +#include +#include + +#endif diff --git a/kaneton/include/library b/kaneton/include/library new file mode 120000 index 0000000..2184626 --- /dev/null +++ b/kaneton/include/library @@ -0,0 +1 @@ +../library/include \ No newline at end of file diff --git a/kaneton/include/machine b/kaneton/include/machine new file mode 120000 index 0000000..6ee46d0 --- /dev/null +++ b/kaneton/include/machine @@ -0,0 +1 @@ +../machine/include \ No newline at end of file diff --git a/kaneton/include/modules b/kaneton/include/modules new file mode 120000 index 0000000..464b823 --- /dev/null +++ b/kaneton/include/modules @@ -0,0 +1 @@ +../modules \ No newline at end of file diff --git a/kaneton/include/platform b/kaneton/include/platform new file mode 120000 index 0000000..2f9cd55 --- /dev/null +++ b/kaneton/include/platform @@ -0,0 +1 @@ +../machine/platform/.current/include \ No newline at end of file diff --git a/kaneton/library/Makefile b/kaneton/library/Makefile new file mode 100644 index 0000000..3e3383a --- /dev/null +++ b/kaneton/library/Makefile @@ -0,0 +1,94 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/kaneton/library/Makefile +# +# created matthieu bucchianeri [sat sep 1 12:40:51 2007] +# updated julien quintard [thu dec 16 11:42:02 2010] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/library + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers + +# +# ---------- variables -------------------------------------------------------- +# + +LIBRARY_C := alloc.c \ + ctype.c \ + format.c \ + memcpy.c \ + memcmp.c \ + memset.c \ + printf.c \ + random.c \ + snprintf.c \ + sprintf.c \ + strchr.c \ + strcmp.c \ + strcpy.c \ + strdup.c \ + strlen.c \ + strncmp.c \ + strncpy.c \ + strrchr.c \ + strtol.c \ + strtoul.c \ + sum2.c + +LIBRARY_O := $(LIBRARY_C:.c=.o) + +LIBRARY_INCLUDE := $(_LIBRARY_INCLUDE_DIR_)/library.h + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: $(_LIBRARY_LO_) + +$(_LIBRARY_LO_): $(LIBRARY_O) + $(call env_remove,$(_LIBRARY_LO_),) + + $(call env_archive,$(_LIBRARY_LO_),$(LIBRARY_O),) + +clear: + $(call env_remove,$(LIBRARY_O),) + + $(call env_remove,$(_LIBRARY_LO_),) + + $(call env_purge,) + +prototypes: + $(call env_prototypes,$(LIBRARY_INCLUDE),) + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(LIBRARY_C),) + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/library/alloc.c b/kaneton/library/alloc.c new file mode 100644 index 0000000..9a16321 --- /dev/null +++ b/kaneton/library/alloc.c @@ -0,0 +1,667 @@ +/* + * licence kaneton licence + * + * project kaneton + * + * file /home/buckman/kaneton/libs/klibrary/libdata/alloc.c + * + * created cedric aubouy [sun sep 25 19:57:33 2005] + * updated matthieu bucchianeri [wed dec 6 00:31:11 2006] + */ + +/* + * ---------- information ----------------------------------------------------- + * XXX + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include +#include +#include + +/* + * ---------- macros ---------------------------------------------------------- + */ + +#define AREA_LIMIT(_area_) \ + (void*)((t_uint8*)(_area_) + sizeof (t_area) + (_area_)->size) + +#define NEXT_CHUNK(_chunk_) \ + (t_chunk*)((t_uint8*)(_chunk_) + sizeof (t_chunk) + (_chunk_)->size) + +#define ROUND_SIZE(_size_) \ + ((_size_) % sizeof (t_vaddr) ? \ + (_size_) + sizeof (t_vaddr) - (_size_) % sizeof (t_vaddr) : \ + (_size_)) + +#define PAGED_SIZE(_size_) \ + ((_size_) % ___kaneton$pagesz ? \ + (_size_) + ___kaneton$pagesz - (_size_) % ___kaneton$pagesz : \ + (_size_)) + +#ifdef MALLOC_SIGN_ENABLE +# define SIGN(_type_,_obj_) \ + (_obj_)->signature = _type_##_SIGNATURE; + +# define CHECK_SIGN(_type_,_obj_) \ + ((_obj_)->signature == _type_##_SIGNATURE) +#else +# define SIGN(_type_,_obj_) +# define CHECK_SIGN(_type_,_obj_) \ + (1) +#endif + +/* + * ---------- globals --------------------------------------------------------- + */ + +t_alloc _alloc; + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the kernel manager. + */ + +extern m_kernel _kernel; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function allocates memory. + * + * steps: + * + * 1) align the size correctly. + * 2) loop through the areas. + * 3) loop through the chunks. + * 4) is the current chunk large enough ? + * a) split the chunk in two if necessary. + * b) compute the address to return. + * c) update the free-list. + * 5) we failed searching into our available chunks. + * a) reserve a new area. + * b) build the area. + * c) build the one or two chunks. + * 6) increment the call counter. + * 7) update the lowest and highest block addresses. + */ + +void* malloc(size_t size) +{ + static int stucked = 0; + t_area* area; + t_area* prev_area = NULL; + t_chunk* chunk; + t_chunk* prev_free = NULL; + t_chunk* next_free; + t_chunk* splitted; + t_vsize pagesz; + t_vaddr addr; + void* allocated = NULL; + + /* + * 1) + */ + + size = ROUND_SIZE(size); + + /* + * 2) + */ + + for (area = _alloc.areas; + area != NULL && allocated == NULL; + prev_area = area, area = area->next_area) + { + if (area->size < size || area->first_free_chunk == NULL) + continue; + + /* + * 3) + */ + + for (chunk = area->first_free_chunk, prev_free = NULL; + chunk != NULL && allocated == NULL; + prev_free = chunk, chunk = chunk->next_free) + { + /* + * 4) + */ + + if (chunk->size >= size) + { + /* + * a) + */ + + if (chunk->size > size) + { + if (chunk->size - size < sizeof (t_chunk)) + { + size = chunk->size; + } + else + { + splitted = (t_chunk*)((t_uint8*)chunk + sizeof (t_chunk) + + size); + splitted->size = chunk->size - size - sizeof (t_chunk); + splitted->next_free = chunk->next_free; + splitted->area = area; + SIGN(CHUNK, splitted); + chunk->size = size; + chunk->next_free = splitted; + } + } + + /* + * b) + */ + + allocated = chunk + 1; + + /* + * c) + */ + + next_free = chunk->next_free; + + if (prev_free == NULL) + { + area->first_free_chunk = next_free; + } + else + { + prev_free->next_free = next_free; + } + } + } + } + + /* + * 5) + */ + + if (allocated == NULL) + { + if (stucked) + { + addr = _alloc.reserve; + + _alloc.reserve = 0; + + pagesz = ___kaneton$pagesz; + } + else + { + + /* + * a) + */ + + pagesz = PAGED_SIZE(size + sizeof (t_area) + sizeof (t_chunk)); + + stucked = 1; + + assert(map_reserve(_kernel.as, + MAP_OPTION_SYSTEM, + pagesz, + PERMISSION_READ | PERMISSION_WRITE, + &addr) == ERROR_OK); + + stucked = 0; + + if (_alloc.reserve == 0) + { + assert(map_reserve(_kernel.as, + MAP_OPTION_SYSTEM, + ___kaneton$pagesz, + PERMISSION_READ | PERMISSION_WRITE, + &_alloc.reserve) == ERROR_OK) + + if (prev_area != NULL) + prev_area = prev_area->next_area; + else + prev_area = _alloc.areas; + } + } + + /* + * b) + */ + + area = (t_area*)addr; + + area->size = pagesz - sizeof (t_area); + area->prev_area = prev_area; + area->next_area = NULL; + SIGN(AREA, area); + + if (prev_area != NULL) + prev_area->next_area = area; + else + _alloc.areas = area; + + /* + * c) + */ + + chunk = (t_chunk*)(area + 1); + chunk->area = area; + chunk->next_free = NULL; + SIGN(CHUNK, chunk); + allocated = chunk + 1; + + if (2 * sizeof (t_chunk) + size < area->size) + { + chunk->size = size; + area->first_free_chunk = NEXT_CHUNK(chunk); + area->first_free_chunk->area = area; + area->first_free_chunk->next_free = NULL; + area->first_free_chunk->size = area->size - size - + 2 * sizeof (t_chunk); + SIGN(CHUNK, area->first_free_chunk); + } + else + { + chunk->size = area->size - sizeof (t_chunk); + area->first_free_chunk = NULL; + } + } + + /* + * 6) + */ + + _alloc.nalloc++; + + /* + * 7) + */ + + if (allocated < _alloc.lowest) + _alloc.lowest = allocated; + + if (allocated > _alloc.highest) + _alloc.highest = allocated; + +#ifdef MALLOC_SIGN_ENABLE + alloc_check_signatures(); +#endif + + return (allocated); +} + +/* + * this fonction releases a memory area. + * + * steps: + * + * 1) test pointer validity. + * 2) look for the previous free chunk in the same area. + * 3) update chunks information. + * 4) try to merge side-by-side free chunks. + * 5) if the area is empty then free it. + * 6) increment the free calls counter. + */ + +void free(void* ptr) +{ + t_chunk* chunk = (t_chunk*)ptr - 1; + t_area* area; + t_chunk* l; + t_chunk* next; + + /* + * 1) + */ + + if (!ptr) + return; + + if (ptr < _alloc.lowest) + { +#ifdef MALLOC_DEBUG + module_call(console, print + "warning: junk pointer, to low to make sense.\n"); +#endif + return; + } + + if (ptr > _alloc.highest) + { +#ifdef MALLOC_DEBUG + module_call(console, print + "warning: junk pointer, to high to make sense.\n"); +#endif + return; + } + + /* + * 2) + */ + + area = chunk->area; + + for (l = area->first_free_chunk; + l != NULL; + l = l->next_free) + { + if (l == chunk) + { + module_call(console, print, + "warning: chunk is already free.\n"); + return; + } + if (l > chunk) + break; + } + + /* + * 3) + */ + + if (l != NULL) + { + chunk->next_free = l->next_free; + l->next_free = chunk; + } + else + { + chunk->next_free = area->first_free_chunk; + area->first_free_chunk = chunk; + } + + /* + * 4) + */ + + if (chunk->next_free == NEXT_CHUNK(chunk)) + { + next = chunk->next_free; + chunk->next_free = next->next_free; + chunk->size += sizeof (t_chunk) + next->size; + } + + if (l != NULL && NEXT_CHUNK(l) == chunk) + { + l->next_free = chunk->next_free; + l->size += sizeof (t_chunk) + chunk->size; + } + + /* + * 5) + */ + + if (area != _alloc.areas && + area->first_free_chunk->size == area->size - sizeof (t_chunk)) + { + area->prev_area->next_area = NULL; + + if (map_release(_kernel.as, (t_vaddr)area) != ERROR_OK) + module_call(console, print, + "warning: unable to release area.\n"); + } + + /* + * 6) + */ + + _alloc.nfree++; + +#ifdef MALLOC_SIGN_ENABLE + alloc_check_signatures(); +#endif +} + +/* + * this function returns the number of allocation calls. + */ + +u_int32_t alloc_nalloc(void) +{ + return (_alloc.nalloc); +} + +/* + * this function returns the number of release calls. + */ + +u_int32_t alloc_nfree(void) +{ + return (_alloc.nfree); +} + +/* + * this function dumps the allocator state. + * + * steps: + * + * 1) display some general information. + * 2) loop through areas. + * 3) loop through chunks in an area. + */ + +void alloc_dump(void) +{ + t_area* area; + t_chunk* chunk; + void* limit; + t_chunk* next; + t_chunk* next_free; + + /* + * 1) + */ + + module_call(console, print, + "allocator dump\n"); + + module_call(console, print, + "calls: %u alloc(), %u free()\n", + _alloc.nalloc, _alloc.nfree); + + module_call(console, print, + "dumping all chunks:\n"); + + /* + * 2) + */ + + for (area = _alloc.areas; + area != NULL; + area = area->next_area) + { + module_call(console, print, + "area: %p, size: %u\n", + area, area->size); + + limit = AREA_LIMIT(area); + next_free = area->first_free_chunk; + + /* + * 3) + */ + + for (chunk = (t_chunk*)(area + 1); + (void*)chunk < limit; + chunk = next) + { + module_call(console, print, + " chunk: %p, user address: %p, size: %u", chunk, + chunk + 1, chunk->size); + if (chunk == next_free) + { + next_free = chunk->next_free; + module_call(console, print, + " FREE\n"); + } + else + module_call(console, print, + "\n"); + + next = NEXT_CHUNK(chunk); + } + } +} + +/* + * this function checks all the signatures. + * + * steps: + * + * 1) loop through areas. + * 2) loop through chunks in an area. + */ + +#ifdef MALLOC_SIGN_ENABLE +void alloc_check_signatures(void) +{ + t_area* area; + t_chunk* chunk; + void* limit; + t_chunk* next; + + /* + * 2) + */ + + for (area = _alloc.areas; + area != NULL; + area = area->next_area) + { + limit = AREA_LIMIT(area); + + if (!CHECK_SIGN(AREA, area)) + module_call(console, print, + "warning: area %p signature is broken\n", + area); + + for (chunk = (t_chunk*)(area + 1); + (void*)chunk < limit; + chunk = next) + { + if (!CHECK_SIGN(CHUNK, chunk)) + module_call(console, print, + "warning: chunk %p signature is broken\n", + area); + + next = NEXT_CHUNK(chunk); + } + } +} +#endif + +/* + * this fonction reallocates memory, meaning this function resizes a memory + * area. + * + * steps: + * + */ + +void* realloc(void* ptr, + size_t size) +{ + t_chunk* chunk = (t_chunk*)ptr - 1; + void* new; + size_t s; + + if (ptr == NULL) + return malloc(size); + + if ((new = malloc(size)) == NULL) + return (NULL); + + if (chunk->size > size) + s = size; + else + s = chunk->size; + + memcpy(new, ptr, s); + + free(ptr); + + return (new); +} + +/* + * this function setup the map_reserve/map_release pointers. + */ + +void alloc_setup(void) +{ + assert(map_reserve(_kernel.as, + MAP_OPTION_SYSTEM, + ___kaneton$pagesz, + PERMISSION_READ | PERMISSION_WRITE, + &_alloc.reserve) == ERROR_OK); +} + +/* + * this function initializes the allocator. + * + * the arguments addr and size specify a kind of survival area. the kernel + * will call this allocator to dynamically reserve memory while no + * kernel memory manager is installed yet. so this area will be used in a + * very special way. + * + * [XXX] although this function receives a physical address, it is assumed + * to be mapped though this should be verified! + * + * steps: + * + * 1) initialize the allocator global structure. + * 2) create the first chunk. + * 3) create the first area and put the first chunk in. + */ + +int alloc_init(paddr_t addr, + psize_t size) +{ + t_area* first_area; + t_chunk* first_chunk; + + /* + * 1) + */ + + memset(&_alloc, 0x0, sizeof (t_alloc)); + + first_area = _alloc.areas = (t_area*)addr; + + _alloc.nalloc = 0; + _alloc.nfree = 0; + _alloc.lowest = (void*)((t_vaddr)-1); + _alloc.highest = (void*)0; + _alloc.reserve = 0; + + /* + * 2) + */ + + first_chunk = (t_chunk*)(first_area + 1); + first_chunk->size = size - sizeof (t_area) - sizeof (t_chunk); + first_chunk->next_free = NULL; + first_chunk->area = first_area; + SIGN(CHUNK, first_chunk); + + /* + * 3) + */ + + first_area->size = size - sizeof (t_area); + first_area->first_free_chunk = first_chunk; + first_area->prev_area = NULL; + first_area->next_area = NULL; + SIGN(AREA, first_area); + + return (0); +} diff --git a/kaneton/library/ctype.c b/kaneton/library/ctype.c new file mode 100644 index 0000000..dce6a53 --- /dev/null +++ b/kaneton/library/ctype.c @@ -0,0 +1,117 @@ +/* + * licence kaneton licence + * + * project kaneton + * + * file /home/buckman/kaneton/libs/klibrary/libstring/ctype.c + * + * created julien quintard [fri feb 11 02:56:44 2005] + * updated matthieu bucchianeri [tue jan 24 11:57:43 2006] + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include +#include +#include + +/* + * ---------- globals --------------------------------------------------------- + */ + +const u_char _ctype[(1 << CHAR_BITS) + 1] = +{ + 0, + _C, _C, _C, _C, _C, _C, _C, _C, + _C, _C|_S, _C|_S, _C|_S, _C|_S, _C|_S, _C, _C, + _C, _C, _C, _C, _C, _C, _C, _C, + _C, _C, _C, _C, _C, _C, _C, _C, + _S|_B, _P, _P, _P, _P, _P, _P, _P, + _P, _P, _P, _P, _P, _P, _P, _P, + _N, _N, _N, _N, _N, _N, _N, _N, + _N, _N, _P, _P, _P, _P, _P, _P, + _P, _U|_X, _U|_X, _U|_X, _U|_X, _U|_X, _U|_X, _U, + _U, _U, _U, _U, _U, _U, _U, _U, + _U, _U, _U, _U, _U, _U, _U, _U, + _U, _U, _U, _P, _P, _P, _P, _P, + _P, _L|_X, _L|_X, _L|_X, _L|_X, _L|_X, _L|_X, _L, + _L, _L, _L, _L, _L, _L, _L, _L, + _L, _L, _L, _L, _L, _L, _L, _L, + _L, _L, _L, _P, _P, _P, _P, _C +}; + +const short _ctype_toupper[(1 << CHAR_BITS) + 1] = +{ + EOF, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +const short _ctype_tolower[(1 << CHAR_BITS) + 1] = +{ + EOF, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', + 'x', 'y', 'z', 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; diff --git a/kaneton/library/format.c b/kaneton/library/format.c new file mode 100644 index 0000000..c5221d9 --- /dev/null +++ b/kaneton/library/format.c @@ -0,0 +1,422 @@ +/* + * licence kaneton licence + * + * project kaneton + * + * file /home/mycure/kaneton/libs/klibrary/libstring/printf.c + * + * created julien quintard [thu may 6 14:37:44 2004] + * updated julien quintard [fri mar 10 03:49:08 2006] + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +int format_string(t_format_character_fn character, + t_format_attribute_fn attribute, + char* string, + unsigned int flags, + int len1, + int len2) +{ + unsigned int written; + int padlen; + int i; + + written = 0; + + if (string == NULL) + return (format_string(character, attribute, + "(null)", flags, len1, len2)); + + for (i = 0; string[i]; i++) + ; + + if (!(flags & FORMAT_DOT)) + { + padlen = len1 - i; + + if (padlen < 0) + padlen = 0; + + if (flags & FORMAT_LADJUST) + padlen = -padlen; + + for (; padlen > 0; padlen--) + if (character != NULL) + { + character(' '); + + written++; + } + } + + for (i = 0; (string[i] != 0) && (i < (len1 != -1 ? len1 : i + 1)); i++) + if (character != NULL) + { + character(string[i]); + + written++; + } + + if (!(flags & FORMAT_DOT)) + { + for (; padlen < 0; padlen++) + if (character != NULL) + { + character(' '); + + written++; + } + } + + return (written); +} + +int format_quad(t_format_character_fn character, + t_format_attribute_fn attribute, + quad_t value, + int base, + int hdl_sign, + unsigned int flags, + int len1, + int len2) +{ + char convert[256]; + unsigned int written; + int padlen; + int caps; + char sign; + u_quad_t v; + int i; + + if (flags & FORMAT_DOT) + FORMAT_SWAP(len1, len2); + + written = 0; + caps = 0; + sign = 0; + v = value; + + if (hdl_sign) + if (value < 0) + { + sign = '-'; + v = -value; + } + + if (base < 0) + { + caps = 1; + base = -base; + } + + for (i = 0; (v != 0) || (i == 0); i++) + { + convert[i] = (caps ? "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" : + "0123456789abcdefghijklmnopqrstuvwxyz" + )[v % (unsigned int) base]; + v = (v / (unsigned int) base); + } + convert[i] = 0; + + padlen = len1 - i; + + if (padlen < 0) + padlen = 0; + + if (flags & FORMAT_LADJUST) + padlen = -padlen; + + if ((flags & FORMAT_ZPAD) && (padlen > 0)) + { + if (sign) + { + if (character != NULL) + { + character(sign); + + written++; + } + sign = 0; + padlen--; + } + + for (; padlen > 0; padlen--) + if (character != NULL) + { + character('0'); + + written++; + } + } + + if (padlen > 0) + { + for (; padlen > 0; padlen--) + if (character != NULL) + { + character(' '); + + written++; + } + } + + if (sign) + if (character != NULL) + { + character(sign); + + written++; + } + + for (i--; i >= 0; i--) + if (character != NULL) + { + character(convert[i]); + + written++; + } + + for (; padlen < 0; padlen++) + { + if (character != NULL) + { + character(' '); + + written++; + } + } + + return (written); +} + +int format(t_format_character_fn character, + t_format_attribute_fn attribute, + const char* fmt, + va_list args) +{ + quad_t quadvalue; + char charvalue; + char* strvalue; + int quadflag; + int longflag; + int starflag; + unsigned int written; + unsigned int flags; + int len1; + int len2; + int i; + char c; + + starflag = 0; + flags = 0; + for (written = 0, i = 0; fmt[i]; i++) + { + quadflag = 0; + longflag = 0; + + switch (fmt[i]) + { + case '%': + { + starflag = 0; + flags = 0; + len1 = -1; + len2 = -1; + + _next_char_: + + c = fmt[++i]; + + switch (c) + { + case 0: + { + return (written); + break; + } + case '-': + { + flags |= FORMAT_LADJUST; + goto _next_char_; + } + case '*': + { + len1 = va_arg(args, int); + starflag = 1; + goto _next_char_; + } + case '.': + { + len2 = len1; + len1 = -1; + flags |= FORMAT_DOT; + + goto _next_char_; + } + case '0': + { + if (starflag) + { + flags |= FORMAT_ZPAD; + goto _next_char_; + } + + if (len1 == -1) + flags |= FORMAT_ZPAD; + } + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + { + if (len1 == -1) + len1 = 0; + len1 = len1 * 10 + c - '0'; + goto _next_char_; + } + case 'q': + { + quadflag = 1; + goto _next_char_; + } + case 'l': + { + longflag = 1; + goto _next_char_; + } + case '#': + { + charvalue = (char) va_arg(args, int); + + if (attribute != NULL) + attribute(charvalue); + + break; + } + case 'b': + { + FORMAT_VALUE(0); + written += format_quad(character, attribute, + quadvalue, 2, 0, flags, + len1, len2); + + break; + } + case 'd': case 'i': case 'D': + { + FORMAT_VALUE(1); + written += format_quad(character, attribute, + quadvalue, 10, 1, flags, + len1, len2); + + break; + } + case 'u': case 'U': + { + FORMAT_VALUE(0); + written += format_quad(character, attribute, + quadvalue, 10, 0, flags, + len1, len2); + + break; + } + case 'o': case 'O': + { + FORMAT_VALUE(0); + written += format_quad(character, attribute, + quadvalue, 8, 0, flags, + len1, len2); + + break; + } + case 'p': case 'x': + { + FORMAT_VALUE(0); + written += format_quad(character, attribute, + quadvalue, 16, 0, flags, + len1, len2); + + break; + } + case 'X': + { + FORMAT_VALUE(0); + written += format_quad(character, attribute, + quadvalue, -16, 0, flags, + len1, len2); + + break; + } + case 's': + { + strvalue = va_arg(args, char*); + + written += format_string(character, attribute, + strvalue, flags, + len1, len2); + + break; + } + case 'c': + { + charvalue = va_arg(args, int); + + if (character != NULL) + { + character(charvalue); + + written++; + } + + break; + } + case '%': + { + if (character != NULL) + { + character(c); + + written++; + } + + break; + } + default: + { + if (character != NULL) + { + character('%'); + character(fmt[i]); + + written += 2; + } + } + } + + break; + } + default: + { + if (character != NULL) + { + character(fmt[i]); + + written++; + } + break; + } + } + } + + va_end(args); + + return (written); +} diff --git a/kaneton/library/include/alloc.h b/kaneton/library/include/alloc.h new file mode 100644 index 0000000..377abb1 --- /dev/null +++ b/kaneton/library/include/alloc.h @@ -0,0 +1,101 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton.STABLE/kaneton/library/include/alloc.h + * + * created julien quintard [sun jun 10 17:33:03 2007] + * updated julien quintard [sun dec 5 15:55:46 2010] + */ + +#ifndef LIBRARY_ALLOC_H +#define LIBRARY_ALLOC_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * area block signature + */ + +#define AREA_SIGNATURE 0xab121337 + +/* + * chunk metadata signature + */ + +#define CHUNK_SIGNATURE 0xBA153E01 + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * chunk forward declaration. + */ + +struct s_chunk; + +/* + * area + */ + +typedef struct s_area +{ + vsize_t size; + +#ifdef MALLOC_SIGN_ENABLE + u_int32_t signature; +#endif + + struct s_chunk* first_free_chunk; + + struct s_area* prev_area; + struct s_area* next_area; +} t_area; + +/* + * chunk + */ + +typedef struct s_chunk +{ + vsize_t size; + +#ifdef MALLOC_SIGN_ENABLE + u_int32_t signature; +#endif + + struct s_chunk* next_free; + + t_area* area; +} t_chunk; + +/* + * alloc global information + */ + +typedef struct +{ + t_area* areas; + + void* lowest; + void* highest; + + vaddr_t reserve; + + u_int32_t nalloc; + u_int32_t nfree; +} t_alloc; + +#endif diff --git a/kaneton/library/include/ctype.h b/kaneton/library/include/ctype.h new file mode 100644 index 0000000..4e3a9ab --- /dev/null +++ b/kaneton/library/include/ctype.h @@ -0,0 +1,66 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton.STABLE/kaneton/library/include/ctype.h + * + * created julien quintard [sun jun 10 17:35:42 2007] + * updated julien quintard [sun dec 5 15:55:58 2010] + */ + +#ifndef LIBRARY_CTYPE_H +#define LIBRARY_CTYPE_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- macros ---------------------------------------------------------- + */ + +#define _U 0x01 +#define _L 0x02 +#define _N 0x04 +#define _S 0x08 +#define _P 0x10 +#define _C 0x20 +#define _X 0x40 +#define _B 0x80 + +/* + * ---------- extern ---------------------------------------------------------- + */ + +extern const u_char _ctype[]; +extern const short _ctype_toupper[]; +extern const short _ctype_tolower[]; + +/* + * ---------- macro functions ------------------------------------------------- + */ + +#define isdigit(c) ((int) ((_ctype + 1)[(int) (c)] & _N)) +#define islower(c) ((int) ((_ctype + 1)[(int) (c)] & _L)) +#define isspace(c) ((int) ((_ctype + 1)[(int) (c)] & _S)) +#define ispunct(c) ((int) ((_ctype + 1)[(int) (c)] & _P)) +#define isupper(c) ((int) ((_ctype + 1)[(int) (c)] & _U)) +#define isalpha(c) ((int) ((_ctype + 1)[(int) (c)] & (_U | _L))) +#define isxdigit(c) ((int) ((_ctype + 1)[(int) (c)] & (_N | _X))) +#define isalnum(c) ((int) ((_ctype + 1)[(int) (c)] & (_U | _L | _N))) +#define isprint(c) ((int) ((_ctype + 1)[(int) (c)] & \ + (_P | _U | _L | _N | _B))) +#define isgraph(c) ((int) ((_ctype + 1)[(int) (c)] & (_P | _U | _L | _N))) +#define iscntrl(c) ((int) ((_ctype + 1)[(int) (c)] & _C)) +#define tolower(c) ((int) ((_ctype_tolower + 1)[(int) (c)])) +#define toupper(c) ((int) ((_ctype_toupper + 1)[(int) (c)])) + +#define isascii(c) ((unsigned) (c) <= 0177) +#define toascii(c) ((c) & 0177) + +#endif diff --git a/kaneton/library/include/format.h b/kaneton/library/include/format.h new file mode 100644 index 0000000..84c2088 --- /dev/null +++ b/kaneton/library/include/format.h @@ -0,0 +1,64 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/library/include/format.h + * + * created julien quintard [sun dec 5 15:04:58 2010] + * updated julien quintard [mon dec 13 14:39:10 2010] + */ + +#ifndef LIBRARY_FORMAT_H +#define LIBRARY_FORMAT_H 1 + +/* + * ---------- types ----------------------------------------------------------- + */ + +typedef void (*t_format_character_fn)(char c); +typedef void (*t_format_attribute_fn)(u_int8_t attr); + +/* + * ---------- macro-functions ------------------------------------------------- + */ + +#define FORMAT_VALUE(_sign_) \ + if (quadflag) \ + quadvalue = va_arg(args, quad_t); \ + else \ + { \ + if ((_sign_)) \ + { \ + if (longflag) \ + quadvalue = (u_quad_t) va_arg(args, long); \ + else \ + quadvalue = (u_quad_t) va_arg(args, int); \ + } \ + else \ + if (longflag) \ + quadvalue = (u_quad_t) va_arg(args, unsigned long); \ + else \ + quadvalue = (u_quad_t) va_arg(args, unsigned int); \ + } + +#define FORMAT_SWAP(_i1_, _i2_) \ + { \ + int _tmp_; \ + \ + _tmp_ = (_i1_); \ + (_i1_) = (_i2_); \ + (_i2_) = _tmp_; \ + } + +/* + * ---------- macros ---------------------------------------------------------- + */ + +#define FORMAT_LADJUST 0x1 +#define FORMAT_ZPAD 0x2 +#define FORMAT_DOT 0x4 + +#endif diff --git a/kaneton/library/include/library.h b/kaneton/library/include/library.h new file mode 100644 index 0000000..17c5f5b --- /dev/null +++ b/kaneton/library/include/library.h @@ -0,0 +1,280 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/library/include/library.h + * + * created julien quintard [sun jun 10 17:31:41 2007] + * updated julien quintard [thu dec 16 11:41:59 2010] + */ + +#ifndef LIBRARY_LIBRARY_H +#define LIBRARY_LIBRARY_H 1 + +/* + * ---------- macros ---------------------------------------------------------- + */ + +#define NULL ((void*)0) + +#ifndef FALSE +#define FALSE (0 == 1) +#endif + +#ifndef TRUE +#define TRUE (1 == 1) +#endif + +#define EOF -1 + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include +#include +#include + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../alloc.c + * ../format.c + * ../memcpy.c + * ../memcmp.c + * ../memset.c + * ../printf.c + * ../random.c + * ../snprintf.c + * ../sprintf.c + * ../strchr.c + * ../strcmp.c + * ../strcpy.c + * ../strdup.c + * ../strlen.c + * ../strncmp.c + * ../strncpy.c + * ../strrchr.c + * ../strtol.c + * ../strtoul.c + * ../sum2.c + */ + +/* + * ../alloc.c + */ + +void* malloc(size_t size); + +void free(void* ptr); + +u_int32_t alloc_nalloc(void); + +u_int32_t alloc_nfree(void); + +void alloc_dump(void); + +void alloc_check_signatures(void); + +void* realloc(void* ptr, + size_t size); + +void alloc_setup(void); + +int alloc_init(paddr_t addr, + psize_t size); + + +/* + * ../format.c + */ + +int format_string(t_format_character_fn character, + t_format_attribute_fn attribute, + char* string, + unsigned int flags, + int len1, + int len2); + +int format_quad(t_format_character_fn character, + t_format_attribute_fn attribute, + quad_t value, + int base, + int hdl_sign, + unsigned int flags, + int len1, + int len2); + +int format(t_format_character_fn character, + t_format_attribute_fn attribute, + const char* fmt, + va_list args); + + +/* + * ../memcpy.c + */ + +void* memcpy(void* dest, + const void* src, + size_t n); + + +/* + * ../memcmp.c + */ + +int memcmp(const void* s1, + const void* s2, + size_t n); + + +/* + * ../memset.c + */ + +void* memset(void* s, + int c, + size_t n); + + +/* + * ../printf.c + */ + +void printf_character(char c); + +int printf(char* fmt, + ...); + + +/* + * ../random.c + */ + +void random_seed(void); + +unsigned long random_generate(void); + + +/* + * ../snprintf.c + */ + +void snprintf_character(char c); + +int snprintf(char* buffer, + int size, + char* fmt, + ...); + + +/* + * ../sprintf.c + */ + +void sprintf_character(char c); + +int sprintf(char* buffer, + char* fmt, + ...); + + +/* + * ../strchr.c + */ + +char* strchr(const char* p, + int ch); + + +/* + * ../strcmp.c + */ + +int strcmp(const char* s1, + const char* s2); + + +/* + * ../strcpy.c + */ + +char* strcpy(char* to, + const char* from); + + +/* + * ../strdup.c + */ + +char* strdup(const char* from); + + +/* + * ../strlen.c + */ + +size_t strlen(const char* str); + + +/* + * ../strncmp.c + */ + +int strncmp(const char* s1, + const char* s2, + size_t n); + + +/* + * ../strncpy.c + */ + +char* strncpy(char* to, + char* from, + size_t n); + + +/* + * ../strrchr.c + */ + +char* strrchr(const char* p, + int ch); + + +/* + * ../strtol.c + */ + +long strtol(const char* nptr, + char** endptr, + int base); + + +/* + * ../strtoul.c + */ + +unsigned long strtoul(const char* nptr, + char** endptr, + int base); + + +/* + * ../sum2.c + */ + +u_int32_t sum2(char *buf, + int size); + + +/* + * eop + */ + +#endif diff --git a/kaneton/library/include/limits.h b/kaneton/library/include/limits.h new file mode 100644 index 0000000..36e1355 --- /dev/null +++ b/kaneton/library/include/limits.h @@ -0,0 +1,104 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton.STABLE/kaneton/library/include/limits.h + * + * created julien quintard [sun jun 10 17:53:28 2007] + * updated julien quintard [sun dec 5 15:58:53 2010] + */ + +#ifndef LIBRARY_LIMITS_H +#define LIBRARY_LIMITS_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- macro-functions ------------------------------------------------- + */ + +/* + * integer types MAX and MIN values. thanks to alexandre becoulet. + */ + +/* + * return max integer value for a type + */ + +#define __MINOF_TYPE(t) ((__typeof__(t))(((__typeof__(t))-1) < 0 ? (((__typeof__(t))1) << (sizeof (__typeof__(t)) * 8 - 1)) : 0)) + +/* + * return min integer value for a type + */ + +#define __MAXOF_TYPE(t) ((__typeof__(t))(((__typeof__(t))-1) < 0 ? ~(((__typeof__(t))1) << (sizeof (__typeof__(t)) * 8 - 1)) : -1)) + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * char type + */ + +#define CHAR_BITS (sizeof (char) * 8) + +#define SCHAR_MIN (__MINOF_TYPE(signed char)) +#define SCHAR_MAX (__MAXOF_TYPE(signed char)) +#define UCHAR_MAX (__MAXOF_TYPE(unsigned char)) +#define CHAR_MIN SCHAR_MIN +#define CHAR_MAX SCHAR_MAX + +/* + * short type + */ + +#define SHORT_BITS (sizeof (short) * 8) +#define SHRT_MIN (__MINOF_TYPE(signed short)) +#define SHRT_MAX (__MAXOF_TYPE(signed short)) +#define USHRT_MAX (__MAXOF_TYPE(unsigned short)) + +/* + * int type + */ + +#define INT_BITS (sizeof (int) * 8) +#define INT_MIN (__MINOF_TYPE(signed int)) +#define INT_MAX (__MAXOF_TYPE(signed int)) +#define UINT_MAX (__MAXOF_TYPE(unsigned int)) + +/* + * long type + */ + +#define LONG_BITS (sizeof (long) * 8) +#define SLONG_MIN (__MINOF_TYPE(signed long)) +#define SLONG_MAX (__MAXOF_TYPE(signed long)) +#define ULONG_MAX (__MAXOF_TYPE(unsigned long)) + +/* + * long long type + */ + +#define LLONG_BITS (sizeof (long long) * 8) +#define LLONG_MIN (__MINOF_TYPE(signed long long)) +#define LLONG_MAX (__MAXOF_TYPE(signed long long)) +#define ULLONG_MAX (__MAXOF_TYPE(unsigned long long)) + +/* + * quad type + */ + +#define QUAD_BITS LLONG_BITS +#define SQUAD_MIN LLONG_MIN +#define SQUAD_MAX LLONG_MAX +#define UQUAD_MAX ULLONG_MAX + +#endif diff --git a/kaneton/library/include/stdarg.h b/kaneton/library/include/stdarg.h new file mode 100644 index 0000000..b7f7a44 --- /dev/null +++ b/kaneton/library/include/stdarg.h @@ -0,0 +1,37 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton.STABLE/kaneton/library/include/stdarg.h + * + * created julien quintard [sun jun 10 18:04:06 2007] + * updated julien quintard [sun dec 5 15:59:11 2010] + */ + +#ifndef LIBRARY_STDARG_H +#define LIBRARY_STDARG_H 1 + +/* + * ---------- types ----------------------------------------------------------- + */ + +typedef __builtin_va_list va_list; + +/* + * ---------- macros ---------------------------------------------------------- + */ + +#define va_end __builtin_va_end +#define va_arg __builtin_va_arg + +/* + * ---------- macro-functions ------------------------------------------------- + */ + +#define va_start(v,l) __builtin_va_start((v),l) +#define va_copy(d,s) __builtin_va_copy((d),(s)) + +#endif diff --git a/kaneton/library/include/types.h b/kaneton/library/include/types.h new file mode 100644 index 0000000..ac89336 --- /dev/null +++ b/kaneton/library/include/types.h @@ -0,0 +1,82 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/library/include/types.h + * + * created julien quintard [sun jun 10 17:37:19 2007] + * updated julien quintard [mon dec 13 15:42:59 2010] + */ + +#ifndef LIBRARY_TYPES_H +#define LIBRARY_TYPES_H 1 + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * XXX + * + * here #ifdef should appear to enable types depending on the underlying + * architecture + * + * more generally, this file should be reviewed! + */ + +typedef unsigned char u_char; +typedef unsigned short u_short; +typedef unsigned int u_int; +typedef unsigned long long int u_long; + +typedef u_int paddr_t; +typedef u_int psize_t; +typedef u_int vaddr_t; +typedef u_int vsize_t; + +typedef char int8_t; +typedef u_char u_int8_t; +typedef short int16_t; +typedef u_short u_int16_t; +typedef int int32_t; +typedef u_int u_int32_t; +typedef long long int int64_t; +typedef u_long u_int64_t; + +typedef u_int8_t uint8_t; +typedef u_int16_t uint16_t; +typedef u_int32_t uint32_t; +typedef u_int64_t uint64_t; + + +typedef u_int register_t; + +typedef int64_t blkcnt_t; /* fs block count */ +typedef u_int32_t blksize_t; /* fs optimal block size */ +typedef char * caddr_t; /* core address */ +typedef int32_t daddr_t; /* disk address */ +typedef u_int32_t dev_t; /* device number */ +typedef u_int32_t gid_t; /* group id */ +typedef u_int32_t ino_t; /* inode number */ +typedef u_int32_t mode_t; /* permissions */ +typedef u_int16_t umode_t; /* extended permissions */ +typedef u_int32_t nlink_t; /* link count */ +typedef u_long off_t; /* file offset */ +typedef int64_t quad_t; /* signed quad */ +typedef u_int64_t u_quad_t; /* unsigned quad */ +typedef int64_t pid_t; /* process id */ +typedef u_long rlim_t; /* resource limit */ +typedef int32_t segsz_t; /* segment size */ +typedef u_int32_t uid_t; /* user id */ + +typedef unsigned int size_t; /* sizeof */ +typedef signed int ssize_t; /* byte count or error */ + +typedef long time_t; + +typedef long long fpos_t; + +#endif diff --git a/kaneton/library/memcmp.c b/kaneton/library/memcmp.c new file mode 100644 index 0000000..0d75586 --- /dev/null +++ b/kaneton/library/memcmp.c @@ -0,0 +1,39 @@ +/* + * licence kaneton licence + * + * project kaneton + * + * file /home/buckman/kaneton/libs/klibrary/libstring/memcmp.c + * + * created julien quintard [fri feb 11 02:56:44 2005] + * updated matthieu bucchianeri [tue jan 24 11:57:23 2006] + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +int memcmp(const void* s1, + const void* s2, + size_t n) +{ + if (n != 0) + { + const u_char* p1 = s1; + const u_char* p2 = s2; + + do + { + if (*p1++ != *p2++) + return (*--p1 - *--p2); + } while (--n != 0); + } + + return (0); +} diff --git a/kaneton/library/memcpy.c b/kaneton/library/memcpy.c new file mode 100644 index 0000000..f01b970 --- /dev/null +++ b/kaneton/library/memcpy.c @@ -0,0 +1,41 @@ +/* + * licence kaneton licence + * + * project kaneton + * + * file /home/buckman/kaneton/libs/klibrary/libstring/memcpy.c + * + * created julien quintard [fri feb 11 02:56:44 2005] + * updated matthieu bucchianeri [tue jan 24 11:57:19 2006] + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +void* memcpy(void* dest, + const void* src, + size_t n) +{ + char* d = dest; + const char* s = src; + u_int i; + + if ((n == 0) || (src == dest)) + return (NULL); + + /* + * XXX bad performances + */ + + for (i = 0; i < n; i++) + d[i] = s[i]; + + return (d); +} diff --git a/kaneton/library/memset.c b/kaneton/library/memset.c new file mode 100644 index 0000000..65a081e --- /dev/null +++ b/kaneton/library/memset.c @@ -0,0 +1,33 @@ +/* + * licence kaneton licence + * + * project kaneton + * + * file /home/buckman/kaneton/libs/klibrary/libstring/memset.c + * + * created julien quintard [fri feb 11 02:56:44 2005] + * updated matthieu bucchianeri [tue jan 24 11:57:15 2006] + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +void* memset(void* s, + int c, + size_t n) +{ + char* d = s; + u_int i; + + for (i = 0; i < n; i++) + d[i] = c; + + return (d); +} diff --git a/kaneton/library/printf.c b/kaneton/library/printf.c new file mode 100644 index 0000000..4e26b01 --- /dev/null +++ b/kaneton/library/printf.c @@ -0,0 +1,44 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/library/printf.c + * + * created julien quintard [thu dec 16 11:16:41 2010] + * updated julien quintard [thu dec 16 11:35:16 2010] + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +void printf_character(char c) +{ + module_call(console, character, c); +} + +int printf(char* fmt, + ...) +{ + va_list args; + int written; + + va_start(args, fmt); + + written = format(printf_character, NULL, + fmt, args); + + va_end(args); + + return (written); +} diff --git a/kaneton/library/random.c b/kaneton/library/random.c new file mode 100644 index 0000000..793fa64 --- /dev/null +++ b/kaneton/library/random.c @@ -0,0 +1,58 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/library/random.c + * + * created julien quintard [fri dec 10 14:03:56 2010] + * updated julien quintard [fri dec 10 14:48:09 2010] + */ + +/* + * ---------- information ----------------------------------------------------- + * XXX + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include +#include + +/* + * ---------- globals --------------------------------------------------------- + */ + +t_uint64 _library_random_seed; + +/* + * ---------- functions ------------------------------------------------------- + */ + +void random_seed(void) +{ + s_clock clock; + t_uint64 rounds; + t_uint64 round; + + assert(clock_current(&clock) == ERROR_OK); + + _library_random_seed = CLOCK_UNIQUE(&clock); + + rounds = random_generate() % 32; + + for (round = 0; round < rounds; round++) + random_generate(); +} + +unsigned long random_generate(void) +{ + _library_random_seed = + (1103515245 * _library_random_seed + 12345) % 18446744073709551615ULL; + + return (_library_random_seed); +} diff --git a/kaneton/library/snprintf.c b/kaneton/library/snprintf.c new file mode 100644 index 0000000..663ebc0 --- /dev/null +++ b/kaneton/library/snprintf.c @@ -0,0 +1,64 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/library/snprintf.c + * + * created julien quintard [thu dec 16 11:16:41 2010] + * updated julien quintard [thu dec 16 11:41:34 2010] + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include +#include + +/* + * ---------- globals --------------------------------------------------------- + */ + +char* snprintf_buffer = NULL; +int snprintf_index = 0; +int snprintf_size = 0; + +/* + * ---------- functions ------------------------------------------------------- + */ + +void snprintf_character(char c) +{ + if ((snprintf_buffer != NULL) && + (snprintf_index < snprintf_size)) + snprintf_buffer[snprintf_index++] = c; +} + +int snprintf(char* buffer, + int size, + char* fmt, + ...) +{ + va_list args; + int written; + + snprintf_buffer = buffer; + snprintf_index = 0; + snprintf_size = size; + + va_start(args, fmt); + + written = format(snprintf_character, NULL, + fmt, args); + + va_end(args); + + snprintf_buffer = NULL; + snprintf_index = 0; + snprintf_size = 0; + + return (written); +} diff --git a/kaneton/library/sprintf.c b/kaneton/library/sprintf.c new file mode 100644 index 0000000..024cd1a --- /dev/null +++ b/kaneton/library/sprintf.c @@ -0,0 +1,59 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/library/sprintf.c + * + * created julien quintard [thu dec 16 11:16:41 2010] + * updated julien quintard [thu dec 16 11:40:17 2010] + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include +#include + +/* + * ---------- globals --------------------------------------------------------- + */ + +char* sprintf_buffer = NULL; +int sprintf_index = 0; + +/* + * ---------- functions ------------------------------------------------------- + */ + +void sprintf_character(char c) +{ + if (sprintf_buffer != NULL) + sprintf_buffer[sprintf_index++] = c; +} + +int sprintf(char* buffer, + char* fmt, + ...) +{ + va_list args; + int written; + + sprintf_buffer = buffer; + sprintf_index = 0; + + va_start(args, fmt); + + written = format(sprintf_character, NULL, + fmt, args); + + va_end(args); + + sprintf_buffer = NULL; + sprintf_index = 0; + + return (written); +} diff --git a/kaneton/library/strchr.c b/kaneton/library/strchr.c new file mode 100644 index 0000000..592b9f5 --- /dev/null +++ b/kaneton/library/strchr.c @@ -0,0 +1,32 @@ +/* + * licence kaneton licence + * + * project kaneton + * + * file /home/buckman/kaneton/libs/klibrary/libstring/strchr.c + * + * created julien quintard [fri feb 11 02:56:44 2005] + * updated matthieu bucchianeri [tue jan 24 11:57:00 2006] + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +char* strchr(const char* p, + int ch) +{ + u_int i; + + for (i = 0; p[i]; i++) + if (p[i] == ch) + return ((char *) p + i); + + return (NULL); +} diff --git a/kaneton/library/strcmp.c b/kaneton/library/strcmp.c new file mode 100644 index 0000000..c38b7fc --- /dev/null +++ b/kaneton/library/strcmp.c @@ -0,0 +1,36 @@ +/* + * licence kaneton licence + * + * project kaneton + * + * file /home/buckman/kaneton/libs/klibrary/libstring/strcmp.c + * + * created julien quintard [fri feb 11 02:56:44 2005] + * updated matthieu bucchianeri [tue jan 24 11:56:57 2006] + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +int strcmp(const char* s1, + const char* s2) +{ + u_int i; + u_int j; + + for (i = 0, j = 0; s1[i] && s2[j]; i++, j++) + if (s1[i] != s2[j]) + return (s1[i] - s2[j]); + + if (s1[i] != s2[j]) + return (s1[i] - s2[j]); + + return (0); +} diff --git a/kaneton/library/strcpy.c b/kaneton/library/strcpy.c new file mode 100644 index 0000000..594bbab --- /dev/null +++ b/kaneton/library/strcpy.c @@ -0,0 +1,32 @@ +/* + * licence kaneton licence + * + * project kaneton + * + * file /home/buckman/kaneton/libs/klibrary/libstring/strcpy.c + * + * created julien quintard [fri feb 11 02:56:44 2005] + * updated matthieu bucchianeri [tue jan 24 11:56:52 2006] + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +char* strcpy(char* to, + const char* from) +{ + u_int i; + + for (i = 0; from[i]; i++) + to[i] = from[i]; + to[i] = 0; + + return (to); +} diff --git a/kaneton/library/strdup.c b/kaneton/library/strdup.c new file mode 100644 index 0000000..c5ce710 --- /dev/null +++ b/kaneton/library/strdup.c @@ -0,0 +1,32 @@ +/* + * licence kaneton licence + * + * project kaneton + * + * file /home/buckman/kaneton/libs/klibrary/libstring/strdup.c + * + * created julien quintard [fri feb 11 02:56:44 2005] + * updated matthieu bucchianeri [tue jan 24 11:56:48 2006] + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +char* strdup(const char* from) +{ + char* to; + + if ((to = malloc((strlen(from) + 1) * sizeof (char))) == NULL) + return (NULL); + + strcpy(to, from); + + return (to); +} diff --git a/kaneton/library/strlen.c b/kaneton/library/strlen.c new file mode 100644 index 0000000..8936b80 --- /dev/null +++ b/kaneton/library/strlen.c @@ -0,0 +1,30 @@ +/* + * licence kaneton licence + * + * project kaneton + * + * file /home/buckman/kaneton/libs/klibrary/libstring/strlen.c + * + * created julien quintard [fri feb 11 02:56:44 2005] + * updated matthieu bucchianeri [tue jan 24 11:56:44 2006] + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +size_t strlen(const char* str) +{ + u_int i; + + for (i = 0; str[i]; i++) + ; + + return (i); +} diff --git a/kaneton/library/strncmp.c b/kaneton/library/strncmp.c new file mode 100644 index 0000000..ca99aa6 --- /dev/null +++ b/kaneton/library/strncmp.c @@ -0,0 +1,40 @@ +/* + * licence kaneton licence + * + * project kaneton + * + * file /home/buckman/kaneton/libs/klibrary/libstring/strncmp.c + * + * created julien quintard [fri feb 11 02:56:44 2005] + * updated matthieu bucchianeri [sat may 5 19:20:22 2007] + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +int strncmp(const char* s1, + const char* s2, + size_t n) +{ + u_int i; + u_int j; + + for (i = 0, j = 0; s1[i] && s2[j] && i < n; i++, j++) + if (s1[i] != s2[j]) + return (s1[i] - s2[j]); + + if (i == n) + return (0); + + if (s1[i] != s2[j]) + return (s1[i] - s2[j]); + + return (0); +} diff --git a/kaneton/library/strncpy.c b/kaneton/library/strncpy.c new file mode 100644 index 0000000..5b0d0df --- /dev/null +++ b/kaneton/library/strncpy.c @@ -0,0 +1,35 @@ +/* + * licence kaneton licence + * + * project kaneton + * + * file /home/buckman/kaneton/libs/klibrary/libstring/strncpy.c + * + * created julien quintard [fri feb 11 02:56:44 2005] + * updated matthieu bucchianeri [tue jan 24 11:56:30 2006] + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +char* strncpy(char* to, + char* from, + size_t n) +{ + u_int i; + + for (i = 0; from[i] && i < n; i++) + to[i] = from[i]; + + if (i < n) + to[i] = 0; + + return (to); +} diff --git a/kaneton/library/strrchr.c b/kaneton/library/strrchr.c new file mode 100644 index 0000000..bf10de6 --- /dev/null +++ b/kaneton/library/strrchr.c @@ -0,0 +1,34 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/buckman/kaneton/library/library/libstring/strrchr.c + * + * created matthieu bucchianeri [mon aug 6 21:54:06 2007] + * updated matthieu bucchianeri [mon aug 6 21:56:48 2007] + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +char* strrchr(const char* p, + int ch) +{ + char* prev = NULL; + + for (; *p != 0; p++) + if (*p == (char)ch) + prev = (char*)p; + + return (prev); +} diff --git a/kaneton/library/strtol.c b/kaneton/library/strtol.c new file mode 100644 index 0000000..1ae444e --- /dev/null +++ b/kaneton/library/strtol.c @@ -0,0 +1,140 @@ +/* + * licence kaneton licence + * + * project kaneton + * + * file /home/buckman/kaneton/libs/klibrary/libstring/strtol.c + * + * created julien quintard [fri feb 11 02:56:44 2005] + * updated matthieu bucchianeri [tue jan 24 11:56:25 2006] + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include +#include +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +long strtol(const char* nptr, + char** endptr, + int base) +{ + const char* s = nptr; + long cutoff; + int cutlim; + long acc; + int neg; + int any; + int c; + + /* + * XXX no check, assume that the string is correct + */ + + do + { + c = (unsigned char) *s++; + } while (isspace(c)); + + if (c == '-') + { + neg = 1; + c = *s++; + } + else + { + neg = 0; + + if (c == '+') + c = *s++; + } + + if ((base == 0 || base == 16) && + (c == '0' && (*s == 'x' || *s == 'X'))) + { + c = s[1]; + s += 2; + base = 16; + } + + if (base == 0) + base = c == '0' ? 8 : 10; + + cutoff = neg ? SLONG_MIN : SLONG_MAX; + cutlim = (int) (cutoff % base); + cutoff /= base; + + if (neg == 1) + { + if (cutlim > 0) + { + cutlim -= base; + cutoff += 1; + } + cutlim = -cutlim; + } + + for (acc = 0, any = 0; ; c = (unsigned char) *s++) + { + if (isdigit(c)) + c -= '0'; + else if (isalpha(c)) + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + else + break; + + if (c >= base) + break; + if (any < 0) + continue; + if (neg == 1) + { + if (acc < cutoff || (acc == cutoff && c > cutlim)) + { + any = -1; + acc = SLONG_MIN; + + /* XXX + errno = E2BIG; + suberrno = ERANGE; + */ + } + else + { + any = 1; + acc *= base; + acc -= c; + } + } + else + { + if (acc > cutoff || (acc == cutoff && c > cutlim)) + { + any = -1; + acc = SLONG_MAX; + + /* XXX + errno = E2BIG; + suberrno = ERANGE; + */ + } + else + { + any = 1; + acc *= base; + acc += c; + } + } + } + + if (endptr != 0) + *endptr = (char*) (any ? s - 1 : nptr); + + return (acc); +} diff --git a/kaneton/library/strtoul.c b/kaneton/library/strtoul.c new file mode 100644 index 0000000..cbec40c --- /dev/null +++ b/kaneton/library/strtoul.c @@ -0,0 +1,111 @@ +/* + * licence kaneton licence + * + * project kaneton + * + * file /home/buckman/kaneton/libs/klibrary/libstring/strtoul.c + * + * created julien quintard [fri feb 11 02:56:44 2005] + * updated matthieu bucchianeri [tue jan 24 11:56:20 2006] + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include +#include +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +unsigned long strtoul(const char* nptr, + char** endptr, + int base) +{ + const char* s = nptr; + unsigned long cutoff; + int cutlim; + unsigned long acc; + int neg; + int any; + int c; + + /* + * XXX no check, assume that the string is correct + */ + + do + { + c = (unsigned char) *s++; + } while (isspace(c)); + + if (c == '-') + { + neg = 1; + c = *s++; + } + else + { + neg = 0; + + if (c == '+') + c = *s++; + } + + if ((base == 0 || base == 16) && + (c == '0' && (*s == 'x' || *s == 'X'))) + { + c = s[1]; + s += 2; + base = 16; + } + + if (base == 0) + base = c == '0' ? 8 : 10; + + cutoff = ULONG_MAX / (unsigned long) base; + cutlim = (int) (ULONG_MAX % (unsigned long) base); + + for (acc = 0, any = 0; ; c = (unsigned char) *s++) + { + if (isdigit(c)) + c -= '0'; + else if (isalpha(c)) + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + else + break; + + if (c >= base) + break; + if (any < 0) + continue; + + if (acc > cutoff || (acc == cutoff && c > cutlim)) + { + any = -1; + acc = ULONG_MAX; + + /* XXX + errno = E2BIG; + suberrno = ERANGE; + */ + } + else + { + any = 1; + acc *= (unsigned long) base; + acc += c; + } + } + + if (neg != 0 && any > 0) + acc = -acc; + + if (endptr != 0) + *endptr = (char*) (any ? s - 1 : nptr); + + return (acc); +} diff --git a/kaneton/library/sum2.c b/kaneton/library/sum2.c new file mode 100644 index 0000000..cb47267 --- /dev/null +++ b/kaneton/library/sum2.c @@ -0,0 +1,46 @@ +/* + * licence kaneton licence + * + * project kaneton + * + * file /home/buckman/kaneton/libs/klibrary/libdata/sum2.c + * + * created julien quintard [fri feb 11 02:56:44 2005] + * updated matthieu bucchianeri [wed jul 26 19:18:50 2006] + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +u_int32_t sum2(char *buf, + int size) +{ + register u_int32_t thecrc; + register u_int32_t total; + register u_char *p; + + /* + * Draft 8 POSIX 1003.2: + * + * s = sum of all bytes + * r = s % 2^16 + (s % 2^32) / 2^16 + * thecrc = (r % 2^16) + r / 2^16 + */ + + thecrc = total = 0; + + for (total += (u_int32_t)size, p = (u_char*)buf; size--; ++p) + thecrc += *p; + + thecrc = (thecrc & 0xffff) + (thecrc >> 16); + thecrc = (thecrc & 0xffff) + (thecrc >> 16); + + return (thecrc); +} diff --git a/kaneton/machine/Makefile b/kaneton/machine/Makefile new file mode 100644 index 0000000..d97c635 --- /dev/null +++ b/kaneton/machine/Makefile @@ -0,0 +1,79 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton.STABLE/kaneton/machine/Makefile +# +# created julien quintard [sun jun 10 14:54:43 2007] +# updated julien quintard [thu nov 18 15:53:28 2010] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/machine + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +SUBDIRS := $(_ARCHITECTURE_DIR_) \ + $(_GLUE_DIR_) \ + $(_PLATFORM_DIR_) + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(_MACHINE_LO_) + +$(_MACHINE_LO_): $(_GLUE_LO_) $(_ARCHITECTURE_LO_) $(_PLATFORM_LO_) + $(call env_remove,$(_MACHINE_LO_),) + + $(call env_archive,$(_MACHINE_LO_), \ + $(_GLUE_LO_) \ + $(_ARCHITECTURE_LO_) \ + $(_PLATFORM_LO_),) + +clear: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,clear,) ; \ + done + + $(call env_remove,$(_MACHINE_LO_),) + + $(call env_purge,) + +prototypes: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,prototypes,) ; \ + done + +headers: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,headers,) ; \ + done + +dependencies: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,,) ; \ + done + +endif diff --git a/kaneton/machine/architecture/ia32/educational/Makefile b/kaneton/machine/architecture/ia32/educational/Makefile new file mode 100644 index 0000000..e9b11f4 --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/Makefile @@ -0,0 +1,102 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kane...e/architecture/ia32/educational/Makefile +# +# created julien quintard [sun dec 10 18:38:17 2006] +# updated julien quintard [sun jan 30 13:06:21 2011] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/machine/architecture + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +ARCHITECTURE_C := architecture.c \ + context.c \ + environment.c \ + gdt.c \ + handler.c \ + idt.c \ + io.c \ + paging.c \ + pd.c \ + pmode.c \ + pt.c \ + segmentation.c \ + tlb.c \ + tss.c + +ARCHITECTURE_O := $(ARCHITECTURE_C:.c=.o) + +ARCHITECTURE_INCLUDE := $(_ARCHITECTURE_INCLUDE_DIR_)/architecture.h \ + $(_ARCHITECTURE_INCLUDE_DIR_)/context.h \ + $(_ARCHITECTURE_INCLUDE_DIR_)/environment.h \ + $(_ARCHITECTURE_INCLUDE_DIR_)/gdt.h \ + $(_ARCHITECTURE_INCLUDE_DIR_)/handler.h \ + $(_ARCHITECTURE_INCLUDE_DIR_)/idt.h \ + $(_ARCHITECTURE_INCLUDE_DIR_)/io.h \ + $(_ARCHITECTURE_INCLUDE_DIR_)/paging.h \ + $(_ARCHITECTURE_INCLUDE_DIR_)/pd.h \ + $(_ARCHITECTURE_INCLUDE_DIR_)/pmode.h \ + $(_ARCHITECTURE_INCLUDE_DIR_)/pt.h \ + $(_ARCHITECTURE_INCLUDE_DIR_)/segmentation.h \ + $(_ARCHITECTURE_INCLUDE_DIR_)/tlb.h \ + $(_ARCHITECTURE_INCLUDE_DIR_)/tss.h + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(_ARCHITECTURE_LO_) + +$(_ARCHITECTURE_LO_): $(ARCHITECTURE_O) + $(call env_remove,$(_ARCHITECTURE_LO_),) + + $(call env_archive,$(_ARCHITECTURE_LO_),$(ARCHITECTURE_O),) + +dependencies: + +clear: + $(call env_remove,$(ARCHITECTURE_O),) + + $(call env_remove,$(_ARCHITECTURE_LO_),) + + $(call env_purge,) + +prototypes: + $(call env_prototypes,$(ARCHITECTURE_INCLUDE),) + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(ARCHITECTURE_C),) + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/machine/architecture/ia32/educational/architecture.c b/kaneton/machine/architecture/ia32/educational/architecture.c new file mode 100644 index 0000000..8d5f26b --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/architecture.c @@ -0,0 +1,66 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...itecture/ia32/educational/architecture.c + * + * created julien quintard [sun jan 16 13:30:38 2011] + * updated julien quintard [sat feb 5 12:52:46 2011] + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the IA32 manager. + */ + +ARCHITECTURE_LINKER_LOCATION(".handler_data") +am _architecture; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function dumps the architecture manager. + */ + +t_error architecture_dump(void) +{ + module_call(console, message, + '#', + "architecture: error(0x%x)\n", + _architecture.error); + + module_call(console, message, + '#', + " kernel: ds(0x%x) pdbr(0x%x)\n", + _architecture.kernel.ds, + _architecture.kernel.pdbr); + + module_call(console, message, + '#', + " kis: base(0x%x) size(0x%x) pointer(0x%x)\n", + _architecture.kernel.kis.base, + _architecture.kernel.kis.size, + _architecture.kernel.kis.pointer); + + module_call(console, message, + '#', + " thread: pdbr(0x%x) pointer(0x%x)\n", + _architecture.thread.pdbr, + _architecture.thread.pointer); + + MACHINE_LEAVE(); +} diff --git a/kaneton/machine/architecture/ia32/educational/context.c b/kaneton/machine/architecture/ia32/educational/context.c new file mode 100644 index 0000000..f134aff --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/context.c @@ -0,0 +1,704 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane.../architecture/ia32/educational/context.c + * + * created renaud voltz [tue apr 4 03:08:03 2006] + * updated julien quintard [mon feb 7 15:53:52 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file contains functions related to the IA32 context management. + * + * the ia32/educational implementation makes use of a single TSS - Task + * State Segment which describes the currently executing context. + * + * the context switch mechanism consists in saving the currently executing + * thread's state and loading the future's. note however that the CPU + * performs some saving/restoring automatically. + * + * basically, things go as follows. a task is running, say in CPL3 i.e a guest + * task. when the timer interrupt occurs, for example, the privilege changes + * from CPL3 to CPL0. the CPU, noticing this change in privilege saves some + * of the thread's context---SS, ESP, EFLAGS, CS, EIP and possible an error + * code---on a special stack referred to as the thread's pile i.e a stack + * specifically used whenever the privilege changes. note that, should no + * change in privilege occur, the registers would be stored on the thread's + * current stack. + * + * at this point, the processor executes the handler shell (cf handler.c) + * which pre-handles an interrupt depending on its nature: exception, IRQ etc. + * besides, the handler shell calls the ARCHITECTURE_CONTEXT_SAVE() + * macro-function which is at the heart of the context switching mechanism. + * + * once the interrupt has been treated, the ARCHITECTURE_CONTEXT_RESTORE() + * macro-function restores the necessary. finally, the 'iret' instruction + * is called. the CPU noticing that a privilege change had occured, restores + * the thread's context by fetching the registers it pushed from the + * thread's pile (or from the thread's stack if the privilege had not changed). + * + * the whole context switch mechanism therefore relies on interrupts. more + * precisely the idea for context switching from a thread A to a thread B + * is to (i) save the thread A's context but this is done naturally when + * A gets interrupted and (ii) change the TSS so that thread B gets referenced, + * hence making the CPU think B got interrupted (instead of A). therefore, + * when returning from the interrupt, the CPU will restore B's context + * rather than A's. this is how A got interrupted, its context saved + * and B's execution got resumed. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the architecture manager. + */ + +extern am _architecture; + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * kernel manager. + */ + +extern m_kernel _kernel; + +/* + * thread manager. + */ + +extern m_thread _thread; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function dumps the given context. + */ + +t_error architecture_context_dump(as_context context) +{ + module_call(console, message, + '#', + "context: ds(0x%x) edi(0x%x) esi(0x%x) ebp(0x%x) _esp(0x%x)\n", + context.ds & 0xffff, + context.edi, + context.esi, + context.ebp, + context._esp); + + module_call(console, message, + '#', + " ebx(0x%x) edx(0x%x) ecx(0x%x) eax(0x%x) error(0x%x)\n", + context.ebx, + context.edx, + context.ecx, + context.eax, + context.error); + + module_call(console, message, + '#', + " eip(0x%x) cs(0x%x) eflags(0x%x) esp(0x%x) ss(0x%x)\n", + context.eip, + context.cs & 0xffff, + context.eflags, + context.esp, + context.ss & 0xffff); + + MACHINE_LEAVE(); +} + +/* + * this function builds the given context, initializes its attributes. + * + * steps: + * + * 1) retrieve the thread and task objects. + * 2) depending on the thread's task class. + * A) if this thread is a kernel thread i.e a ring0 thread, this means + * that its context will be saved on its stack. besides, since no change + * in privilege will occur when interrupted, there is no need for a + * pile... + * a) set the pile attributes to zero. + * B) otherwise... + * a) set the thread's pile size. + * b) allocate a pile for the thread i.e a stack which is used by the + * processor to store the context whenever the execution privilege + * changes; for example whenever a guest task running in CP3 is + * interrupted by the timer. + * c) set the pile pointer to the end since IA32 stacks grow towards + * the lower addresses. + * 3) initialize the IA32 context's registers to zero. + * 4) initialize the eflags by activating the first bit (mandatory) but + * also the IF flags to that maskable interrupts get triggered. besides, + * allow driver tasks to perform I/O operations by setting the + * appropriate IOPL. + * 5) set the context's segment selectors according to the task's class. + * 6) complete the thread's initial IA32 context by setting ESP and EIP. + * 7) set the static stack pointer to the end since stacks grow towards + * the lower addresses. + * 8) depending on the thread's task class. + * A) if the thread is a kernel thread, set the initial context's position + * on the stack. indeed, let's recall that ring0 thread's contexts + * are stored on their stack since no change in privilege occurs. + * B) otherwise, set its position on the thread's pile i.e special kernel + * stack. + * 9) finally set the thread's initial IA32 context. note that this step + * is ignored for the kernel thread. indeed, this thread is the one + * setting up the whole kernel. once the scheduler is started, an interrupt + * will be triggered hence interrupting the running thread i.e the kernel + * thread, hence saving its context. since this special thread will always + * start with its context being saved, there is no need to do it now. + * on the contrary, the other threads will begin with their context being + * restored in order to be scheduled for the first time. + */ + +t_error architecture_context_build(i_thread id) +{ + o_task* task; + o_thread* thread; + as_context ctx; + + /* + * 1) + */ + + if (thread_get(id, &thread) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the thread object"); + + if (task_get(thread->task, &task) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + if (task->class == TASK_CLASS_KERNEL) + { + /* + * A) + */ + + /* + * a) + */ + + thread->machine.pile.base = 0x0; + thread->machine.pile.size = 0x0; + thread->machine.pile.pointer = 0x0; + } + else + { + /* + * B) + */ + + /* + * a) + */ + + thread->machine.pile.size = ARCHITECTURE_HANDLER_PILE_SIZE; + + /* + * b) + */ + + if (map_reserve(task->as, + MAP_OPTION_NONE, + thread->machine.pile.size, + PERMISSION_READ | PERMISSION_WRITE, + &thread->machine.pile.base) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve a map for the thread's pile"); + + /* + * c) + */ + + thread->machine.pile.pointer = + thread->machine.pile.base + thread->machine.pile.size - 16; + } + + /* + * 3) + */ + + memset(&ctx, 0x0, sizeof (as_context)); + + /* + * 4) + */ + + ctx.eflags = + ARCHITECTURE_REGISTER_EFLAGS_01 | + ARCHITECTURE_REGISTER_EFLAGS_IF; + + if (task->class == TASK_CLASS_DRIVER) + { + ctx.eflags |= + ARCHITECTURE_REGISTER_EFLAGS_IOPL_SET(ARCHITECTURE_PRIVILEGE_DRIVER); + } + + /* + * 5) + */ + + switch (task->class) + { + case TASK_CLASS_KERNEL: + { + ctx.cs = _thread.machine.selectors.kernel.cs; + ctx.ds = _thread.machine.selectors.kernel.ds; + ctx.ss = _thread.machine.selectors.kernel.ds; + + break; + } + case TASK_CLASS_DRIVER: + { + ctx.cs = _thread.machine.selectors.driver.cs; + ctx.ds = _thread.machine.selectors.driver.ds; + ctx.ss = _thread.machine.selectors.driver.ds; + + break; + } + case TASK_CLASS_SERVICE: + { + ctx.cs = _thread.machine.selectors.service.cs; + ctx.ds = _thread.machine.selectors.service.ds; + ctx.ss = _thread.machine.selectors.service.ds; + + break; + } + case TASK_CLASS_GUEST: + { + ctx.cs = _thread.machine.selectors.guest.cs; + ctx.ds = _thread.machine.selectors.guest.ds; + ctx.ss = _thread.machine.selectors.guest.ds; + + break; + } + } + + /* + * 6) + */ + + ctx.esp = thread->stack.base + thread->stack.size - 16; + ctx.eip = thread->entry; + + /* + * 7) + */ + + thread->machine.stack.pointer = thread->stack.base + thread->stack.size - 16; + + /* + * 8) + */ + + if (task->class == TASK_CLASS_KERNEL) + { + /* + * A) + */ + + thread->machine.context = + thread->machine.stack.pointer - sizeof (as_context); + } + else + { + /* + * B) + */ + + thread->machine.context = + thread->machine.pile.pointer - sizeof (as_context); + } + + /* + * 9) + */ + + if (thread->id != _kernel.thread) + { + if (architecture_context_set(thread->id, &ctx) != ERROR_OK) + MACHINE_ESCAPE("unable to set the context"); + } + + MACHINE_LEAVE(); +} + +/* + * this function destroys a thread's context. + * + * steps: + * + * 1) retrieve the thread and task object. + * 2) if the thread has a pile---i.e is not a kernel thread---release it. + */ + +t_error architecture_context_destroy(i_thread id) +{ + o_task* task; + o_thread* thread; + + /* + * 1) + */ + + if (thread_get(id, &thread) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the thread object"); + + if (task_get(thread->task, &task) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + if (task->class != TASK_CLASS_KERNEL) + { + if (map_release(task->as, + thread->machine.pile.base) != ERROR_OK) + MACHINE_ESCAPE("unable to release the thread's pile"); + } + + MACHINE_LEAVE(); +} + +/* + * this function sets up the context switch mechanism. + * + * steps: + * + * 1) retrieve the kernel address space object. + * 2) reserve the KIS - Kernel Interrupt Stack and set its pointer to + * the end since stacks grow towards low addresses. note that this + * special stack resides within the kernel, as its name indicates, and + * is used to handle interrupts. + * 3) reserve a memory area for the TSS. + * 4) build the initial TSS. + * 5) update the TSS in order to represent the currently executing thread. + * since the current thread is the kernel thread, which runs in ring0, + * no ring0 stack needs to be provided, hence SS0 and ESP0 are ignored. + * 6) activate the TSS. + * 7) retrieve the segment selectors associated with the + * kernel/driver/service/guest code/data segments. these segment selectors + * will be used whenever a thread of the associated task class will + * be created. + */ + +t_error architecture_context_setup(void) +{ + as_tss* tss; + o_as* as; + + /* + * 1) + */ + + if (as_get(_kernel.as, &as) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the address space object"); + + /* + * 2) + */ + + _architecture.kernel.kis.size = ARCHITECTURE_HANDLER_KIS_SIZE; + + if (map_reserve(_kernel.as, + MAP_OPTION_SYSTEM, + _architecture.kernel.kis.size, + PERMISSION_READ | PERMISSION_WRITE, + &_architecture.kernel.kis.base) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve the TSS memory area"); + + _architecture.kernel.kis.pointer = + _architecture.kernel.kis.base + (_architecture.kernel.kis.size - 16); + + /* + * 3) + */ + + if (map_reserve(_kernel.as, + MAP_OPTION_SYSTEM, + ARCHITECTURE_TSS_SIZE, + PERMISSION_READ | PERMISSION_WRITE, + &_thread.machine.tss) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve the TSS memory area"); + + /* + * 4) + */ + + if (architecture_tss_build(_thread.machine.tss, &tss) != ERROR_OK) + MACHINE_ESCAPE("unable to build the initial TSS"); + + /* + * 5) + */ + + if (architecture_tss_update(tss, + ARCHITECTURE_TSS_SS0_NULL, + ARCHITECTURE_TSS_ESP0_NULL, + ARCHITECTURE_TSS_IO) != ERROR_OK) + MACHINE_ESCAPE("unable to build the TSS"); + + /* + * 6) + */ + + if (architecture_tss_activate(tss) != ERROR_OK) + MACHINE_ESCAPE("unable to activate the system's TSS"); + + /* + * 7) + */ + + if (architecture_gdt_selector( + ARCHITECTURE_GDT_INDEX_KERNEL_CODE, + ARCHITECTURE_PRIVILEGE_KERNEL, + &_thread.machine.selectors.kernel.cs) != ERROR_OK) + MACHINE_ESCAPE("unable to build the kernel code segment selector"); + + if (architecture_gdt_selector( + ARCHITECTURE_GDT_INDEX_KERNEL_DATA, + ARCHITECTURE_PRIVILEGE_KERNEL, + &_thread.machine.selectors.kernel.ds) != ERROR_OK) + MACHINE_ESCAPE("unable to build the kernel data segment selector"); + + if (architecture_gdt_selector( + ARCHITECTURE_GDT_INDEX_DRIVER_CODE, + ARCHITECTURE_PRIVILEGE_DRIVER, + &_thread.machine.selectors.driver.cs) != ERROR_OK) + MACHINE_ESCAPE("unable to build the driver code segment selector"); + + if (architecture_gdt_selector( + ARCHITECTURE_GDT_INDEX_DRIVER_DATA, + ARCHITECTURE_PRIVILEGE_DRIVER, + &_thread.machine.selectors.driver.ds) != ERROR_OK) + MACHINE_ESCAPE("unable to build the driver data segment selector"); + + if (architecture_gdt_selector( + ARCHITECTURE_GDT_INDEX_SERVICE_CODE, + ARCHITECTURE_PRIVILEGE_SERVICE, + &_thread.machine.selectors.service.cs) != ERROR_OK) + MACHINE_ESCAPE("unable to build the service code segment selector"); + + if (architecture_gdt_selector( + ARCHITECTURE_GDT_INDEX_SERVICE_DATA, + ARCHITECTURE_PRIVILEGE_SERVICE, + &_thread.machine.selectors.service.ds) != ERROR_OK) + MACHINE_ESCAPE("unable to build the service data segment selector"); + + if (architecture_gdt_selector( + ARCHITECTURE_GDT_INDEX_GUEST_CODE, + ARCHITECTURE_PRIVILEGE_GUEST, + &_thread.machine.selectors.guest.cs) != ERROR_OK) + MACHINE_ESCAPE("unable to build the guest code segment selector"); + + if (architecture_gdt_selector( + ARCHITECTURE_GDT_INDEX_GUEST_DATA, + ARCHITECTURE_PRIVILEGE_GUEST, + &_thread.machine.selectors.guest.ds) != ERROR_OK) + MACHINE_ESCAPE("unable to build the guest data segment selector"); + + MACHINE_LEAVE(); +} + +/* + * this function switches execution to the specified thread. + */ + +t_error architecture_context_switch(i_thread current, + i_thread future) +{ + /* FIXME[code to complete] */ + + MACHINE_LEAVE(); +} + +/* + * this function pushes the given arguments on a thread's stack. this way, + * the thread will be able to access these values at startup. + * + * steps: + * + * 1) retrieve the thread and task objects. + * 2) retrieve the thread's context. + * 3) update the thread's stack pointer by decreasing it since the + * arguments are going to be stored at the top of it. + * 4) write the arguments to the thread's stack. + * 5) update the thread's context. + */ + +t_error architecture_context_arguments(i_thread id, + void* arguments, + t_vsize size) +{ + s_thread_context context; + o_thread* thread; + o_task* task; + + /* + * 1) + */ + + if (thread_get(id, &thread) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the thread object"); + + if (task_get(thread->task, &task) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + if (thread_store(thread->id, &context) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the thread context"); + + /* + * 3) + */ + + context.sp -= size; + + /* + * 4) + */ + + if (as_write(task->as, arguments, size, context.sp) != ERROR_OK) + MACHINE_ESCAPE("unable to store the arguments on the thread's stack"); + + /* + * 5) + */ + + if (thread_load(thread->id, context) != ERROR_OK) + MACHINE_ESCAPE("unable to update the thread context"); + + MACHINE_LEAVE(); +} + +/* + * this function retrieves the IA32 context of the given thread. + * + * note that the interrupted task's context has been stored in its + * pile, i.e ring0 stack (excepts for kernels threads). since the pile is + * only mapped in the task's address space, this function, running in the + * kernel environment, cannot access it directly. therefore, the as_read() + * function is used to temporarily map the necessary pages in the kernel + * address space. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the thread and task objects. + * 2) read the thread's context from its pile. + */ + +t_error architecture_context_get(i_thread id, + as_context* context) +{ + o_thread* thread; + o_task* task; + + /* + * 0) + */ + + if (context == NULL) + MACHINE_ESCAPE("the 'context' argument is null"); + + /* + * 1) + */ + + if (thread_get(id, &thread) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the thread object"); + + if (task_get(thread->task, &task) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + if (as_read(task->as, + thread->machine.context, + sizeof (as_context), + context) != ERROR_OK) + MACHINE_ESCAPE("unable to read the thread's IA32 context"); + + MACHINE_LEAVE(); +} + +/* + * this function updates the context of a given thread. + * + * note that the interrupted task's context has been stored in its + * pile, i.e ring0 stack (except for kernel threads). since the pile is + * only mapped in the task's address space, this function, running in the + * kernel environment, cannot access it directly. therefore, the as_write() + * function is used to temporarily map the necessary pages in the kernel + * address space. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the thread and task objects. + * 2) update the thread's context stored in its pile by writing its address + * space. + */ + +t_error architecture_context_set(i_thread id, + as_context* context) +{ + o_thread* thread; + o_task* task; + + /* + * 0) + */ + + if (context == NULL) + MACHINE_ESCAPE("the 'context' argument is null"); + + /* + * 1) + */ + + if (thread_get(id, &thread) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the thread object"); + + if (task_get(thread->task, &task) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + if (as_write(task->as, + context, + sizeof (as_context), + thread->machine.context) != ERROR_OK) + MACHINE_ESCAPE("unable to write the thread's IA32 context"); + + MACHINE_LEAVE(); +} diff --git a/kaneton/machine/architecture/ia32/educational/environment.c b/kaneton/machine/architecture/ia32/educational/environment.c new file mode 100644 index 0000000..d8a42f5 --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/environment.c @@ -0,0 +1,444 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...hitecture/ia32/educational/environment.c + * + * created julien quintard [thu jan 13 23:13:50 2011] + * updated julien quintard [tue apr 12 07:40:26 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file contains functions for initializing the environments of a + * task, especially regarding its address space. + * + * note that the kernel is treated separately from the servers i.e drivers, + * services and guests. + * + * [XXX:improvement] in the server initialization, needless to map the kernel + * code and stack. instead the handler shells should be + * mapped i.e the .handler section. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the init structure. + */ + +extern s_init* _init; + +/* + * the kernel manager. + */ + +extern m_kernel _kernel; + +/* + * the thread manager. + */ + +extern m_thread _thread; + +/* + * the segment manager. + */ + +extern m_segment _segment; + +/* + * the architecture manager. + */ + +extern am _architecture; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function initializes the kernel's environment. + * + * steps: + * + * 1) retrieve the address space objec. + * 2) set the kernel address space's page directory by importing the + * page directory set up by the boot loader. + * 3) generate the PDBR - Page Directory Base Register, also known as + * the CR3, based on the kernel page directory's physical address and + * some flags. + * 4) set the page directory virtual address as being an identity mapping + * of the physical address. this is how the boot loader set things up. + * 5) set the current page directory as being the kernel's one by updating + * the microprocessor CR3 register. + * 6) update the kernel page directory---which is assumed to have been mapped + * by the boot loader through the identity mapping technique---in order + * to set up the mirroring entry. this entry wastes the last 4MB + * of memory and are used for accessing the kernel page directory and + * tables without mapping anything, hence preventing infinite loops. + * note that the entry references the page directory itself, making the + * page directory act as a page table whenever accessed through the + * mirror page directory entry. + * 7) the last 4MB of virtual memory are not accessible since the mirroring + * page directory entry and the referenced page table's entries---the + * page directory itself---are used for the mirroring mechanism. these + * 4MB are wasted and must therefore not be reservable or the kernel may + * end up overwritten the mirroring entries. a region covering the last + * 4MB of virtual memory is therefore injected. + * note that the region injected references an ID_UNUSED-identified + * segment in order to avoid having to reserve a 4MB segment. this is + * possible because region_inject() does not check if the referenced + * segment identifer is valid. + * 8) this step consists in cleaning the page directory set up by the boot + * loader, now used by the kernel, by unmapping any page which is not + * related to the fundamental regions provided by the boot loader. + * go through all the pre-reserved regions provided by the bootloader plus + * one. this additional iteration is required in order to clean the + * mapped pages from the last region to the end of the virtual address + * space. + * a) compute the page directory and table end indexes for the given + * region. note that for the extra iteration, the end indexes are + * set to their maximum so that every page table entry of every page + * directory entry following the last region is cleaned. + * b) go through the involved page directory entries. + * i) if the page directory entry does not reference a page table or + * is used as the mirroring entry, leave it. otherwise... + * #1) retrieve the page table referenced by the page directory entry. + * note that the boot loader relied on the identity mapping technique + * for its paging set up. identity mapping is therefore used to + * retrieve the page table virtual address. + * #2) go through the page table's involved entries. + * #a) if the page table entry is used, delete the reference as + * this mapping must not be very important since not related to + * the pre-reserved regions provided by the boot loader. + * c) if the treated region is not the extra one, compute the next + * page directory and table start indexes as starting right after + * the end of the region i.e address + size. + * 9) flush the whole TLB, resetting all the address translations. + * 10) register the kernel PDBR as being the PDBR on which to switch whenever + * an interrupt occurs. + */ + +t_error architecture_environment_kernel(i_as id) +{ + i_region useless; + at_cr3 pdbr; + o_as* as; + o_region* r; + + /* + * 1) + */ + + if (as_get(id, &as) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the address space object"); + + /* + * 2) + */ + + as->machine.pd = _init->machine.pd; + + /* + * 3) + */ + + if (architecture_paging_pdbr(as->machine.pd, + ARCHITECTURE_REGISTER_CR3_PCE | + ARCHITECTURE_REGISTER_CR3_PWB, + &pdbr) != ERROR_OK) + MACHINE_ESCAPE("unable to build the CR3 register's content"); + + /* + * 5) + */ + + /* FIXME[make the page directory provided by the boot loader the system's + current page directory by updating the necessary IA32 hardware + structure and possibly storing the value in a globally accessible + variable such as a manager] */ + + /* + * 6) + */ + + /* FIXME[create the mirroring entry by adding the page directory's + address] */ + + /* + * 7) + */ + + if ((r = malloc(sizeof (o_region))) == NULL) + MACHINE_ESCAPE("unable to allocate memory for the region object"); + + r->address = ARCHITECTURE_PAGING_ADDRESS(ARCHITECTURE_PD_MIRROR, 0); + r->segment = ID_UNUSED; + r->offset = 0x0; + r->size = ARCHITECTURE_PT_SIZE * ___kaneton$pagesz; + r->options = REGION_OPTION_NONE; + + if (region_inject(as->id, r, &useless) != ERROR_OK) + MACHINE_ESCAPE("unable to inject the mirroring region"); + + /* + * 8) + */ + + /* FIXME[go through the registered regions and remove the + page table entries which do not correspond to these + regions. this is necessary because the boot loader + mapped an awful lot of pages which must now be cleaned] + + pde.start = 0; + pte.start = 0; + + for (i = 0; i < (_init->nregions + 1); i++) + { + if (i != _init->nregions) + { + pde.end = ARCHITECTURE_PD_INDEX(_init->regions[i].address); + pte.end = ARCHITECTURE_PT_INDEX(_init->regions[i].address); + } + else + { + pde.end = ARCHITECTURE_PD_SIZE - 1; + pte.end = ARCHITECTURE_PT_SIZE; + } + + for (pde.index = pde.start; + pde.index <= pde.end; + pde.index++) + { + if ((pde.index != ARCHITECTURE_PD_MIRROR) && + (pd[pde.index] & ARCHITECTURE_PDE_PRESENT)) + { + pt = (at_pt)ARCHITECTURE_PDE_ADDRESS(pd[pde.index]); + + for (pte.index = (pde.index == pde.start ? pte.start : 0); + pte.index < (pde.index == pde.end ? + pte.end : ARCHITECTURE_PT_SIZE); + pte.index++) + { + if (pt[pte.index] & ARCHITECTURE_PTE_PRESENT) + { + if (architecture_pt_delete(pt, pte.index) != ERROR_OK) + MACHINE_ESCAPE("unable to delete the page " + "table entry"); + } + } + } + } + + if (i != _init->nregions) + { + pde.start = ARCHITECTURE_PD_INDEX(_init->regions[i].address + + _init->regions[i].size); + pte.start = ARCHITECTURE_PT_INDEX(_init->regions[i].address + + _init->regions[i].size); + } + } + */ + + /* + * 9) + */ + + if (architecture_tlb_flush() != ERROR_OK) + MACHINE_ESCAPE("unable to flush the TLB"); + + /* + * 10) + */ + + _architecture.kernel.pdbr = pdbr; + + MACHINE_LEAVE(); +} + +/* + * this function sets up the environment of a server i.e drivers, services + * and guests. + * + * steps: + * + * 1) retrieve the address space object. + * 2) reserve a system segment. + * 3) use this segment for the given address space's page directory. + * 4) map the page directory, initialize it and unmap it. + * 5) locate the segment containing the system's TSS and map it in + * the given address space. note that the TSS is mapped at the same + * virtual address as in the kernel. + * 6) locate the segment containing the system's GDT and map it in the + * given address space, again at the same virtual address as the kernel's. + * 7) locate the segment containing the system's IDT and map it in the + * given address space, again at the same virtual address as the kernel's. + * 8) locate the segment containing the kernel code and map it in the given + * address space. note that the identity mapping technique is used here. + * 9) locate the segment containing the kernel stack and map it in the given + * address space, note that the identity mapping technique is used here. + */ + +t_error architecture_environment_server(i_as id) +{ + i_segment segment; + i_region region; + o_as* as; + o_region* r; + o_segment* s; + + /* + * 1) + */ + + if (as_get(id, &as) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the address space object"); + + /* + * 2) + */ + + if (segment_reserve(as->id, + ___kaneton$pagesz, + PERMISSION_READ | PERMISSION_WRITE, + SEGMENT_OPTION_SYSTEM, + &segment) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve a segment"); + + if (segment_get(segment, &s) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the segment object"); + + /* + * 3) + */ + + as->machine.pd = s->address; + + /* + * 4) + */ + + /* FIXME[map the server's page directory, initialize it and + unmap it] */ + + /* + * 5) + */ + + if (region_locate(_kernel.as, + _thread.machine.tss, + ®ion) == ERROR_FALSE) + MACHINE_ESCAPE("unable to locate the region in which the TSS lies"); + + if (region_get(_kernel.as, region, &r) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the region object"); + + if (region_reserve(as->id, + r->segment, + 0x0, + REGION_OPTION_FORCE | + REGION_OPTION_NONE, + _thread.machine.tss, + r->size, + ®ion) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve the region mapping the TSS"); + + /* + * 6) + */ + + if (region_locate(_kernel.as, + (t_vaddr)_segment.machine.gdt.table, + ®ion) == ERROR_FALSE) + MACHINE_ESCAPE("unable to locate the region in which the GDT lies"); + + if (region_get(_kernel.as, region, &r) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the region object"); + + if (region_reserve(as->id, + r->segment, + 0x0, + REGION_OPTION_FORCE | + REGION_OPTION_NONE, + (t_vaddr)_segment.machine.gdt.table, + ___kaneton$pagesz, + ®ion) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve the region mapping the GDT"); + + /* + * 7) + */ + + /* FIXME[reserve a region for the system's IDT very much as for + the GDT above] */ + + /* + * 8) + */ + + /* XXX + if (region_reserve(asid, + _init->kcode, + LINKER_SYMBOL(_handler_begin) - _init->kcode, + REGION_OPTION_FORCE | REGION_OPTION_PRIVILEGED, + LINKER_SYMBOL(_handler_begin), + LINKER_SYMBOL(_handler_end) - + LINKER_SYMBOL(_handler_begin), + ®) != ERROR_OK) + + if (region_reserve(asid, + _init->kcode, + LINKER_SYMBOL(_handler_data_begin) - _init->kcode, + REGION_OPTION_FORCE | REGION_OPTION_PRIVILEGED, + LINKER_SYMBOL(_handler_data_begin), + LINKER_SYMBOL(_handler_data_end) - + LINKER_SYMBOL(_handler_data_begin), + ®) != ERROR_OK) + */ + + if (segment_locate(_init->kcode, &segment) == ERROR_FALSE) + MACHINE_ESCAPE("unable to locate the segment which contains the " + "kernel code"); + + if (region_reserve(as->id, + segment, + 0x0, + REGION_OPTION_FORCE, + (t_vaddr)_init->kcode, + (t_vsize)_init->kcodesz, + ®ion) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve the region mapping the kernel code"); + + /* + * 9) + */ + + if (segment_locate(_init->kstack, + &segment) == ERROR_FALSE) + MACHINE_ESCAPE("unable to locate the segment which contains the " + "kernel stack"); + + if (region_reserve(as->id, + segment, + 0x0, + REGION_OPTION_FORCE, + (t_vaddr)_init->kstack, + (t_vsize)_init->kstacksz, + ®ion) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve the region mapping the kernel stack"); + + MACHINE_LEAVE(); +} diff --git a/kaneton/machine/architecture/ia32/educational/gdt.c b/kaneton/machine/architecture/ia32/educational/gdt.c new file mode 100644 index 0000000..e725164 --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/gdt.c @@ -0,0 +1,531 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...hine/architecture/ia32/educational/gdt.c + * + * created matthieu bucchianeri [mon dec 10 13:54:28 2007] + * updated julien quintard [sun jan 30 20:44:09 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file contains functionalities for managing the GDT - Global Descriptor + * Table. + * + * note that the educational implementation retrieves the GDT provided + * by the boot loader in order to re-use it. however, the entries set + * by the boot loaders are then replaced by the kernel's. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the segment manager which contains the current GDT. + */ + +extern m_segment _segment; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function dumps a GDT given by its structure's address. + * + * steps: + * + * 1) display a header message. + * 2) go through the GDT entries. + * a) ignore non-present entries. + * b) compute the base and limit addresses. + * c) build the flags string. + * d) depending on the segment type flags, build a type string. + * A) the entry represents a system segment. + * B) the entry represents a regular code/data segment. + * e) display the segment. + */ + +t_error architecture_gdt_dump(void) +{ + t_uint16 i; + + /* + * 1) + */ + + module_call(console, message, + '#', "GDT: table(0x%08x) size(%u)\n", + _segment.machine.gdt.table, + _segment.machine.gdt.size); + + /* + * 2) + */ + + for (i = 0; i < _segment.machine.gdt.size; i++) + { + t_privilege privilege; + t_paddr base; + t_paddr limit; + char* type; + char flags[4]; + + /* + * a) + */ + + if (!(_segment.machine.gdt.table[i] & ARCHITECTURE_GDTE_PRESENT)) + continue; + + /* + * b) + */ + + base = ARCHITECTURE_GDTE_BASE_GET(_segment.machine.gdt.table[i]); + + limit = ARCHITECTURE_GDTE_LIMIT_GET(_segment.machine.gdt.table[i]); + + if (_segment.machine.gdt.table[i] & ARCHITECTURE_GDTE_GRANULARITY) + limit *= ___kaneton$pagesz; + + privilege = ARCHITECTURE_GDTE_DPL_GET(_segment.machine.gdt.table[i]); + + /* + * c) + */ + + if (_segment.machine.gdt.table[i] & ARCHITECTURE_GDTE_AVAILABLE) + flags[0] = 'f'; + else + flags[0] = '.'; + + if (_segment.machine.gdt.table[i] & ARCHITECTURE_GDTE_32BIT) + flags[1] = 's'; + else + flags[1] = '.'; + + if (_segment.machine.gdt.table[i] & ARCHITECTURE_GDTE_GRANULARITY) + flags[2] = 'g'; + else + flags[2] = '.'; + + flags[3] = '\0'; + + /* + * d) + */ + + if (!(_segment.machine.gdt.table[i] & ARCHITECTURE_GDTE_S)) + { + /* + * A) + */ + + switch (ARCHITECTURE_GDTE_TYPE_SYSTEM(_segment.machine.gdt.table[i])) + { + case ARCHITECTURE_GDTE_LDT: + { + type = "ldt"; + + break; + } + case ARCHITECTURE_GDTE_TSS_AVAILABLE: + case ARCHITECTURE_GDTE_TSS_BUSY: + { + type = "tss"; + + break; + } + case ARCHITECTURE_GDTE_CALL: + { + type = "call gate"; + + break; + } + case ARCHITECTURE_GDTE_TRAP: + { + type = "trap gate"; + + break; + } + case ARCHITECTURE_GDTE_INTERRUPT: + { + type = "interrupt gate"; + + break; + } + default: + MACHINE_ESCAPE("unknown system segment type"); + } + } + else + { + /* + * B) + */ + + switch (ARCHITECTURE_GDTE_TYPE_SEGMENT(_segment.machine.gdt.table[i])) + { + case ARCHITECTURE_GDTE_CODE: + { + type = "code"; + + break; + } + case ARCHITECTURE_GDTE_DATA: + { + type = "data"; + + break; + } + default: + MACHINE_ESCAPE("unknown regular segment type"); + } + } + + /* + * e) + */ + + module_call(console, message, + '#', " %u: base(0x%08x) limit(0x%08x) type(%s) " + "privilege(%u) flags(%s)\n", + i, base, limit, type, privilege, flags); + } + + MACHINE_LEAVE(); +} + +/* + * this function builds a GDT according to the given parameters. + * + * steps: + * + * 0) verify the arguments. + * 1) align the base address if necessary. + * 2) initialized the gdt structure. + * 3) initialize the table's memory. + */ + +t_error architecture_gdt_build(t_paddr base, + t_psize size, + as_gdt* gdt) +{ + /* + * 0) + */ + + if (gdt == NULL) + MACHINE_ESCAPE("the 'gdt' argument is null"); + + if (size > (ARCHITECTURE_GDT_SIZE * sizeof (at_gdte))) + MACHINE_ESCAPE("the given size is too large as exceeding the GDT's " + "theoretically maximum capacity"); + + /* + * 1) + */ + + if (base % sizeof (at_gdte)) + base += sizeof (at_gdte) - (base % sizeof (at_gdte)); + + /* + * 2) + */ + + gdt->table = (at_gdte*)base; + gdt->size = size / sizeof (at_gdte); + + /* + * 3) + */ + + memset(gdt->table, 0x0, gdt->size * sizeof (at_gdte)); + + MACHINE_LEAVE(); +} + +/* + * this function imports the given GDT, making it the current GDT. + * + * steps: + * + * 0) verify the arguments. + * 1) set the GDT register according to the given GDT. + * 2) load the GDT. + * 3) set the given GDT as being the current one by copying its content + * in the current system GDT's structure. + */ + +t_error architecture_gdt_import(as_gdt* gdt) +{ + as_gdtr gdtr; + + /* + * 0) + */ + + if (gdt == NULL) + MACHINE_ESCAPE("the 'gdt' argument is null"); + + /* + * 1) + */ + + gdtr.address = (t_paddr)gdt->table; + gdtr.size = gdt->size * sizeof (at_gdte); + + /* + * 2) + */ + + ARCHITECTURE_LGDT(gdtr); + + /* + * 3) + */ + + memcpy(&_segment.machine.gdt, gdt, sizeof (as_gdt)); + + MACHINE_LEAVE(); +} + +/* + * this function exports the current GDT by copying its content in the + * given structure. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the GDT register's value. + * 2) copy the current GDT's content into the given one. + * 3) updates the given GDT's size. + */ + +t_error architecture_gdt_export(as_gdt* gdt) +{ + as_gdtr gdtr; + at_gdte* source; + at_gdte* dest; + + /* + * 0) + */ + + if (gdt == NULL) + MACHINE_ESCAPE("the 'gdt' argument is null"); + + /* + * 1) + */ + + ARCHITECTURE_SGDT(gdtr); + + /* + * 2) + */ + + source = (at_gdte*)gdtr.address; + dest = gdt->table; + + memcpy(dest, source, gdtr.size); + + /* + * 3) + */ + + gdt->size = gdtr.size / sizeof (at_gdte); + + MACHINE_LEAVE(); +} + +/* + * this function inserts a segment in the table, at a precise index, hence + * possibly erasing a previously recorded segment. + * + * steps: + * + * 0) verify the arguments. + * 1) update the GDT entry. + * 2) set the limit and granularity according to its value. + */ + +t_error architecture_gdt_insert(t_uint16 index, + t_paddr base, + t_paddr limit, + t_flags flags) +{ + /* + * 0) + */ + + if (index >= _segment.machine.gdt.size) + MACHINE_ESCAPE("out-of-bound insertion"); + + if (index == 0) + MACHINE_ESCAPE("the first GDT entry cannot be used"); + + if (_segment.machine.gdt.table[index] & ARCHITECTURE_GDTE_PRESENT) + MACHINE_ESCAPE("the GDT entry to update is already in use"); + + /* + * 1) + */ + + _segment.machine.gdt.table[index] = + ARCHITECTURE_GDTE_PRESENT | + ARCHITECTURE_GDTE_BASE_SET(base) | + ARCHITECTURE_GDTE_32BIT | + flags; + + /* + * 2) + */ + + if (limit >= ___kaneton$pagesz) + { + _segment.machine.gdt.table[index] |= + ARCHITECTURE_GDTE_GRANULARITY | + ARCHITECTURE_GDTE_LIMIT_SET(limit / ___kaneton$pagesz); + } + else + { + _segment.machine.gdt.table[index] |= ARCHITECTURE_GDTE_LIMIT_SET(limit); + } + + MACHINE_LEAVE(); +} + +/* + * this function reserves an available slot for the given segment. + * + * steps: + * + * 0) verify the arguments. + * 1) look for an available slot. + * 2) insert the segment in the GDT. + */ + +t_error architecture_gdt_reserve(t_paddr base, + t_paddr limit, + t_flags flags, + t_uint16* index) +{ + t_uint16 i; + + /* + * 0) + */ + + if (index == NULL) + MACHINE_ESCAPE("the 'index' argument is null"); + + /* + * 1) + */ + + *index = 0; + + for (i = 1; i < _segment.machine.gdt.size; i++) + if (!(_segment.machine.gdt.table[i] & ARCHITECTURE_GDTE_PRESENT)) + { + *index = i; + + break; + } + + if (*index == 0) + MACHINE_ESCAPE("unable to find an available GDT entry"); + + /* + * 2) + */ + + if (architecture_gdt_insert(*index, + base, limit, flags) != ERROR_OK) + MACHINE_ESCAPE("unable to insert the segment in the GDT"); + + MACHINE_LEAVE(); +} + +/* + * this function deletes a GDT entry, making it available. + * + * steps: + * + * 0) verify the arguments. + * 1) re-initialize the GDT entry's memory. + */ + +t_error architecture_gdt_delete(t_uint16 index) +{ + /* + * 0) + */ + + if (index >= _segment.machine.gdt.size) + MACHINE_ESCAPE("out-of-bound insertion"); + + if (index == 0) + MACHINE_ESCAPE("the first GDT entry cannot be used"); + + if (!(_segment.machine.gdt.table[index] & ARCHITECTURE_GDTE_PRESENT)) + MACHINE_ESCAPE("the GDT entry to delete is not present"); + + /* + * 1) + */ + + memset(&_segment.machine.gdt.table[index], 0x0, sizeof (at_gdte)); + + MACHINE_LEAVE(); +} + +/* + * this function builds a segment selector according to the given + * parameters. + * + * steps: + * + * 0) verify the arguments. + * 1) compute the selector according to the given parameters. + */ + +t_error architecture_gdt_selector(t_uint16 index, + t_privilege privilege, + t_uint16* selector) +{ + /* + * 0) + */ + + if (selector == NULL) + MACHINE_ESCAPE("the 'selector' argument is null"); + + if (index >= _segment.machine.gdt.size) + MACHINE_ESCAPE("out-of-bound index"); + + /* + * 1) + */ + + *selector = + ARCHITECTURE_GDT_SELECTOR_TI_GDT | + ARCHITECTURE_GDT_SELECTOR_RPL(privilege) | + ARCHITECTURE_GDT_SELECTOR_INDEX(index); + + MACHINE_LEAVE(); +} diff --git a/kaneton/machine/architecture/ia32/educational/handler.c b/kaneton/machine/architecture/ia32/educational/handler.c new file mode 100644 index 0000000..63002b8 --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/handler.c @@ -0,0 +1,24 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane.../architecture/ia32/educational/handler.c + * + * created renaud voltz [thu feb 23 10:49:43 2006] + * updated julien quintard [mon apr 11 13:44:48 2011] + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* FIXME[complete if necessary] */ diff --git a/kaneton/machine/architecture/ia32/educational/idt.c b/kaneton/machine/architecture/ia32/educational/idt.c new file mode 100644 index 0000000..3b857f2 --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/idt.c @@ -0,0 +1,34 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...hine/architecture/ia32/educational/idt.c + * + * created renaud voltz [sun feb 12 02:02:19 2006] + * updated julien quintard [mon apr 11 13:44:56 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file provides functionalities for managing the IDT - Interrupt + * Descriptor Table. + * + * for more information regarding the handlers triggered through the IDT, + * please have a look at the handler.c file. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* FIXME[complete if necessary] */ diff --git a/kaneton/machine/architecture/ia32/educational/include/architecture.h b/kaneton/machine/architecture/ia32/educational/include/architecture.h new file mode 100644 index 0000000..0e101af --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/include/architecture.h @@ -0,0 +1,182 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane.../ia32/educational/include/architecture.h + * + * created julien quintard [thu jun 7 12:02:10 2007] + * updated julien quintard [sat feb 5 12:52:42 2011] + */ + +#ifndef ARCHITECTURE_ARCHITECTURE_H +#define ARCHITECTURE_ARCHITECTURE_H 1 + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * this macro defines the architecture's endianness i.e little endian. + */ + +#define ___kaneton$endian ENDIAN_LITTLE + +/* + * the frame size defines the fundamental unit of physical memory. in the + * IA32 architecture, this unit is the byte. + */ + +#define ___kaneton$framesz 1 + +/* + * the page size defines the fundamental unit of virtual memory. because of + * the way the IA32 architecture handles pagination through a hierarchy of + * page directory, page tables and page frames, the fundamental unit of + * such memory is the page i.e a 4096-byte of aligned memory. + */ + +#define ___kaneton$pagesz 4096 + +/* + * this macro defines the architecture's word size i.e 32-bit. + */ + +#define ___kaneton$wordsz 32 + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * this structure contains the architecture-manager-specific information, + * especially regarding the whole interrupt and context switch mechanism. + * + * the manager's attributes have the particularity to be placed at a very + * specific location within the kernel binary, in the '.handler_data' + * section. note that in addition, the interrupt shells are placed in + * the '.handler_code' section. this way, a task mapping both sections will + * have access to enough information in order to switch to the kernel + * environment whenever an interrupt occurs so that this interrupt can be + * treated. + * + * this particularity explains why these attributes have not been included + * in the kernel manager's machine-specific data structure i.e + * _kernel.machine.*. + * + * the following details the attributes embedded in this structure. + * + * the 'kernel.ds' attribute (@am_handler + 0) represents the kernel DS - Data + * Segment selector. this selector is required in order to indicate the + * CPU where the real interrupt handler lies i.e within the kernel data + * segment. + * + * the 'kernel.pdbr' (@am_handler + 2) represents the PDBR - Page Directory + * Base Register, also referred to as CR3, to which the system must switch in + * order to treat the interrupt. this is the kernel address space since all + * interrupt handlers are located within the kernel's address space. + * + * the 'kernel.kis.base' contains the address of the stack used for treating + * interrupts. this stack is located within the kernel and is jumped on once + * the interrupt shell has switched the address space to the kernel's. + * + * this 'kernel.kis.size' contains the size of the KIS - Kernel Interrupt + * Stack. + * + * the 'kernel.kis.pointer' (@ + 14) contains the stack pointer within the + * KIS - Kernel Interrupt Stack. whenever an interrupt is to be treated, the + * CPU stack pointer is placed at the address i.e within the KIS. + * + * the 'thread.pdbr' attribute (@ + 18) contains the PDBR of the thread + * which has been interrupted. + * + * the 'thread.pointer' attribute (@ + 22) contains the value of the ESP + * register. note that this value references either the interrupted thread's + * stack or pile depending on its privileges. indeed, while the context + * of ring0 threads is stored in their current stack (no privilege change), + * it is stored in the pile for threads from other rings. + * + * note that the 'thread.*' attributes represent either the state of the + * thread which has just been interrupted or of the thread which is about + * to be resumed. indeed, while the ARCHITECTURE_CONTEXT_SAVE() macro-function + * updates these attributes, the ARCHITECTURE_CONTEXT_RESTORE() uses them + * in order to know which thread to resume. therefore, the process of + * context switching comes down to updating two attributes: thread.pdbr and + * thread.pointer. + * + * finally, the 'error' attribute represents the error code which the CPU + * pushes on the stack for some exceptions. + * + * note that this structure is accessed by the ARCHITECTURE_CONTEXT_SAVE() + * and ARCHITECTURE_CONTEXT_RESTORE() macro-functions, in assembly. since + * the position of the attributes matter, please take care to adjust the + * macro-functions according to your modifications. + */ + +typedef struct +{ + struct + { + t_uint16 ds; + at_cr3 pdbr; + + struct + { + t_vaddr base; + t_vsize size; + t_reg32 pointer; + } kis; + } __attribute__ ((packed)) kernel; + + struct + { + at_cr3 pdbr; + t_reg32 pointer; + } __attribute__ ((packed)) thread; + + t_uint32 error; +} __attribute__ ((packed)) am; + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../architecture.c + */ + +/* + * ../architecture.c + */ + +t_error architecture_dump(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/architecture/ia32/educational/include/asm.h b/kaneton/machine/architecture/ia32/educational/include/asm.h new file mode 100644 index 0000000..3d4cca3 --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/include/asm.h @@ -0,0 +1,120 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...hitecture/ia32/educational/include/asm.h + * + * created julien quintard [fri feb 11 03:04:40 2005] + * updated julien quintard [sun jan 16 08:49:45 2011] + */ + +#ifndef ARCHITECTURE_ASM_H +#define ARCHITECTURE_ASM_H 1 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * these macro-functions provide an easy way to perform some hardware + * operations. + */ + +#define ARCHITECTURE_IRET() \ + asm volatile ("iret"); + +#define ARCHITECTURE_LEAVE() \ + asm volatile ("leave"); + +#define ARCHITECTURE_CLI() \ + asm volatile ("cli") + +#define ARCHITECTURE_STI() \ + asm volatile ("sti") + +#define ARCHITECTURE_LGDT(_var_) \ + asm volatile ("lgdt %0" \ + : \ + : "m" (_var_)) + +#define ARCHITECTURE_SGDT(_var_) \ + asm volatile ("sgdt %0" \ + : "=m" (_var_) \ + :) + +#define ARCHITECTURE_LIDT(_var_) \ + asm volatile ("lidt %0" \ + : \ + : "m" (_var_)) + +#define ARCHITECTURE_SIDT(_var_) \ + asm volatile ("sidt %0" \ + : "=m" (_var_) \ + :) + +#define ARCHITECTURE_LCR2(_var_) \ + asm volatile ("movl %0, %%eax\n" \ + "movl %%eax, %%cr2" \ + : \ + : "m" (_var_)) + +#define ARCHITECTURE_SCR2(_var_) \ + asm volatile ("movl %%cr2, %%eax\n" \ + "movl %%eax, %0" \ + : "=m" (_var_) \ + :) + +#define ARCHITECTURE_LCR3(_var_) \ + asm volatile ("movl %0, %%eax\n" \ + "movl %%eax, %%cr3" \ + : \ + : "m" (_var_)) + +#define ARCHITECTURE_SCR3(_var_) \ + asm volatile ("movl %%cr3, %%eax\n" \ + "movl %%eax, %0" \ + : "=m" (_var_) \ + :) + +#define ARCHITECTURE_LTR(_var_) \ + asm volatile ("ltr %0" \ + : \ + : "m" (_var_)) + +#define ARCHITECTURE_HLT() \ + asm volatile ("hlt") + +#define ARCHITECTURE_OUTB(_port_, _data_) \ + asm volatile ("outb %%al, %%dx" \ + : \ + : "d" ((t_uint16)(_port_)), "a" (_data_)) + +#define ARCHITECTURE_OUTW(_port_, _data_) \ + asm volatile ("outw %%ax, %%dx" \ + : \ + : "d" ((t_uint16)(_port_)), "a" (_data_)) + +#define ARCHITECTURE_OUTL(_port_, _data_) \ + asm volatile ("outl %%eax, %%dx" \ + : \ + : "d" ((t_uint16)(_port_)), "a" (_data_)) + +#define ARCHITECTURE_INB(_port_, _data_) \ + asm volatile ("inb %%dx, %%al" \ + : "=a" (_data_) \ + : "d" ((t_uint16)(_port_))) + +#define ARCHITECTURE_INW(_port_, _data_) \ + asm volatile ("inw %%dx, %%ax" \ + : "=a" (_data_) \ + : "d" ((t_uint16)(_port_))) + +#define ARCHITECTURE_INL(_port_, _data_) \ + asm volatile ("inl %%dx, %%eax" \ + : "=a" (_data_) \ + : "d" ((t_uint16)(_port_))) + +#endif diff --git a/kaneton/machine/architecture/ia32/educational/include/context.h b/kaneton/machine/architecture/ia32/educational/include/context.h new file mode 100644 index 0000000..13fd0df --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/include/context.h @@ -0,0 +1,117 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...cture/ia32/educational/include/context.h + * + * created renaud voltz [tue apr 4 22:01:00 2006] + * updated julien quintard [mon feb 7 16:19:17 2011] + */ + +#ifndef ARCHITECTURE_CONTEXT_H +#define ARCHITECTURE_CONTEXT_H 1 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function saves the context of the thread which had just + * been interrupted. + * + * note that at this point, the stack in use is the thread's pile i.e ring0 + * stack; except for the kernel threads since there is no change in privilege. + */ + +#define ARCHITECTURE_CONTEXT_SAVE() \ + /* FIXME[code to complete] */ + +/* + * this macro-function restores the context of the thread whose PDBR and pile + * are referenced in _architecture.thread. as such, the whole ia32/educational + * context switch mechanism relies on the simple fact that changing + * the _architecture structure and returning from the interrupt will make + * the thread's context restored and its execution resumed. + * + * note that at this point, the environment is composed of the kernel PDBR + * and the KIS - Kernel Interrupt Stack. + */ + +#define ARCHITECTURE_CONTEXT_RESTORE() \ + /* FIXME[code to complete] */ + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * this structure represents the IA32 context. + */ + +typedef struct +{ + t_reg32 ds; + t_reg32 edi; + t_reg32 esi; + t_reg32 ebp; + t_reg32 _esp; + t_reg32 ebx; + t_reg32 edx; + t_reg32 ecx; + t_reg32 eax; + t_reg32 error; + t_reg32 eip; + t_reg32 cs; + t_reg32 eflags; + t_reg32 esp; + t_reg32 ss; +} __attribute__ ((packed)) as_context; + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../context.c + */ + +/* + * ../context.c + */ + +t_error architecture_context_dump(as_context context); + +t_error architecture_context_build(i_thread id); + +t_error architecture_context_destroy(i_thread id); + +t_error architecture_context_setup(void); + +t_error architecture_context_locate(void); + +t_error architecture_context_switch(i_thread current, + i_thread future); + +t_error architecture_context_arguments(i_thread id, + void* arguments, + t_vsize size); + +t_error architecture_context_get(i_thread id, + as_context* context); + +t_error architecture_context_set(i_thread id, + as_context* context); + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/architecture/ia32/educational/include/environment.h b/kaneton/machine/architecture/ia32/educational/include/environment.h new file mode 100644 index 0000000..f1780e0 --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/include/environment.h @@ -0,0 +1,36 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...e/ia32/educational/include/environment.h + * + * created julien quintard [fri jan 14 11:14:31 2011] + * updated julien quintard [fri jan 14 11:14:54 2011] + */ + +#ifndef ARCHITECTURE_ENVIRONMENT_H +#define ARCHITECTURE_ENVIRONMENT_H + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../environment.c + */ + +/* + * ../environment.c + */ + +t_error architecture_environment_kernel(i_as id); + +t_error architecture_environment_server(i_as id); + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/architecture/ia32/educational/include/gdt.h b/kaneton/machine/architecture/ia32/educational/include/gdt.h new file mode 100644 index 0000000..d8e4873 --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/include/gdt.h @@ -0,0 +1,281 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...hitecture/ia32/educational/include/gdt.h + * + * created julien quintard [fri feb 11 03:04:40 2005] + * updated julien quintard [sun jan 16 01:02:49 2011] + */ + +#ifndef ARCHITECTURE_GDT_H +#define ARCHITECTURE_GDT_H 1 + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * this macro represents the maximum number of entries a GDT can contain. + */ + +#define ARCHITECTURE_GDT_SIZE 256 + +/* + * the code/data segment types. + */ + +#define ARCHITECTURE_GDTE_CODE ((1LL << 43) | \ + (1LL << 41)) +#define ARCHITECTURE_GDTE_DATA (1LL << 41) + +/* + * the system segment types. + */ + +#define ARCHITECTURE_GDTE_LDT (1LL << 41) +#define ARCHITECTURE_GDTE_TSS ((1LL << 43) | \ + (1LL << 40)) +#define ARCHITECTURE_GDTE_TSS_AVAILABLE ARCHITECTURE_GDTE_TSS +#define ARCHITECTURE_GDTE_TSS_BUSY ((1LL << 43) | \ + (1LL << 41) | \ + (1LL << 40)) + +#define ARCHITECTURE_GDTE_CALL ((1LL << 43) | \ + (1LL << 42)) +#define ARCHITECTURE_GDTE_TRAP ((1LL << 43) | \ + (1LL << 42) | \ + (1LL << 41) | \ + (1LL << 40)) +#define ARCHITECTURE_GDTE_INTERRUPT ((1LL << 43) | \ + (1LL << 42) | \ + (1LL << 41)) + +/* + * type definitions. + * + * the S bit indicates that the segment is not a system one. the present + * flag indicates that the segment is a valid one and must therefore be + * considered by the CPU. + */ + +#define ARCHITECTURE_GDTE_S (1LL << 44) +#define ARCHITECTURE_GDTE_PRESENT (1LL << 47) + +/* + * flags definitions. + * + * the available bit can be used by the software for its own purpose. the + * 16bit/32bit flag indicates whether the system operates in 16/32 bit. the + * meaning of this bit is actually more complicated than that. for more + * information, please refer to the IA32 documentation. finally, the granular + * bit indicates whether the limit address should be considered as a number + * of bytes or a number of pages. + */ + +#define ARCHITECTURE_GDTE_AVAILABLE (1LL << 52) +#define ARCHITECTURE_GDTE_16BIT (0LL << 54) +#define ARCHITECTURE_GDTE_32BIT (1LL << 54) +#define ARCHITECTURE_GDTE_GRANULARITY (1LL << 55) + +/* + * these values indicates the table in which is located the selector's target. + */ + +#define ARCHITECTURE_GDT_SELECTOR_TI_GDT (0LL << 2) +#define ARCHITECTURE_GDT_SELECTOR_TI_LDT (1LL << 2) + +/* + * these definitions represents the indexes of the different system + * segments, for the kernel, driver, service and guest task classes. + */ + +#define ARCHITECTURE_GDT_INDEX_KERNEL_CODE 1 +#define ARCHITECTURE_GDT_INDEX_KERNEL_DATA 2 +#define ARCHITECTURE_GDT_INDEX_DRIVER_CODE 3 +#define ARCHITECTURE_GDT_INDEX_DRIVER_DATA 4 +#define ARCHITECTURE_GDT_INDEX_SERVICE_CODE 5 +#define ARCHITECTURE_GDT_INDEX_SERVICE_DATA 6 +#define ARCHITECTURE_GDT_INDEX_GUEST_CODE 7 +#define ARCHITECTURE_GDT_INDEX_GUEST_DATA 8 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function computes the hardware DPL - Descriptor Privilege + * Level according to a system-based privilege value. + * + * the GET macro does the opposite, return the system privilege based on + * the GDT entry's type. + */ + +#define ARCHITECTURE_GDTE_DPL_SET(_privilege_) \ + (((at_gdte)(_privilege_) & 0x3) << 45) + +#define ARCHITECTURE_GDTE_DPL_GET(_gdte_) \ + (((_gdte_) >> 45) & 0x3) + +/* + * these two macro-function returns a system type value according to + * the hardware 8-bit field. + * + * the first macro-function must be used if the segment is a system one + * while the second must be used for regular code/data segments. + */ + +#define ARCHITECTURE_GDTE_TYPE_SYSTEM(_gdte_) \ + ((_gdte_) & \ + (ARCHITECTURE_GDTE_LDT | \ + ARCHITECTURE_GDTE_TSS | \ + ARCHITECTURE_GDTE_CALL | \ + ARCHITECTURE_GDTE_TRAP | \ + ARCHITECTURE_GDTE_INTERRUPT)) + +#define ARCHITECTURE_GDTE_TYPE_SEGMENT(_gdte_) \ + ((_gdte_) & \ + (ARCHITECTURE_GDTE_CODE | \ + ARCHITECTURE_GDTE_DATA)) + +/* + * this macro-function sets the base address in its GDT entry form. + */ + +#define ARCHITECTURE_GDTE_BASE_SET(_base_) \ + (at_gdte)((((at_gdte)(_base_) & 0x0000ffff) << 16) | \ + (((at_gdte)(_base_) & 0x00ff0000) << 16) | \ + (((at_gdte)(_base_) & 0xff000000) << 32)) + +/* + * this macro-function returns the base address contained in the given + * GDT entry. + */ + +#define ARCHITECTURE_GDTE_BASE_GET(_gdte_) \ + (t_paddr)((((_gdte_) >> 16) & 0x0000ffff) | \ + (((_gdte_) >> 16) & 0x00ff0000) | \ + (((_gdte_) >> 32) & 0xff000000)) + +/* + * this macro-function sets the limit address in its GDT entry form. + */ + +#define ARCHITECTURE_GDTE_LIMIT_GET(_gdte_) \ + (t_paddr)((((_gdte_) >> 00) & 0x0000ffff) | \ + (((_gdte_) >> 32) & 0x000f0000)) + +/* + * this macro-function returns the limit address contained in the given + * GDT entry. + */ + +#define ARCHITECTURE_GDTE_LIMIT_SET(_limit_) \ + (at_gdte)((((at_gdte)(_limit_) & 0x0000ffff) << 00) | \ + (((at_gdte)(_limit_) & 0x00ff0000) << 32)) + +/* + * this macro-function computes the RPL - Request Privilege Level value + * according to the given system privilege value. + */ + +#define ARCHITECTURE_GDT_SELECTOR_RPL(_privilege_) \ + ((_privilege_) << 0) + +/* + * this macro-function compute the selector's index field which indicates + * the GDT entry attached to the selector. + */ + +#define ARCHITECTURE_GDT_SELECTOR_INDEX(_index_) \ + ((_index_) << 3) + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include +#include + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * the hardware-represented GDT entry which is composed of a base address, + * a limit address, a type and some flags. + * + * for more information, please refer to the IA32 hardware documentation. + */ + +typedef t_uint64 at_gdte; + +/* + * the GTD descriptor i.e the structure for managing a GDT. + * + * this structure contains a pointer to the GDT entries along with the + * current size i.e number of allocated entries. + */ + +typedef struct +{ + at_gdte* table; + t_uint16 size; +} as_gdt; + +/* + * this structure represents the GDTR - GDT Register which is the hardware + * structure locating the current GDT. + */ + +typedef struct +{ + t_uint16 size; + t_paddr address; +} __attribute__ ((packed)) as_gdtr; + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../gdt.c + */ + +/* + * ../gdt.c + */ + +t_error architecture_gdt_dump(void); + +t_error architecture_gdt_build(t_paddr base, + t_psize size, + as_gdt* gdt); + +t_error architecture_gdt_import(as_gdt* gdt); + +t_error architecture_gdt_export(as_gdt* gdt); + +t_error architecture_gdt_insert(t_uint16 index, + t_paddr base, + t_paddr limit, + t_flags flags); + +t_error architecture_gdt_reserve(t_paddr base, + t_paddr limit, + t_flags flags, + t_uint16* index); + +t_error architecture_gdt_delete(t_uint16 index); + +t_error architecture_gdt_selector(t_uint16 index, + t_privilege privilege, + t_uint16* selector); + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/architecture/ia32/educational/include/handler.h b/kaneton/machine/architecture/ia32/educational/include/handler.h new file mode 100644 index 0000000..1bbf8c1 --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/include/handler.h @@ -0,0 +1,56 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...cture/ia32/educational/include/handler.h + * + * created renaud voltz [fri feb 17 16:48:22 2006] + * updated julien quintard [mon apr 11 13:45:51 2011] + */ + +#ifndef ARCHITECTURE_HANDLER_H +#define ARCHITECTURE_HANDLER_H 1 + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * this macro represents the number of handlers the system must set up, + * for the exceptions, IRQs, IPIs and syscalls. + */ + +#define ARCHITECTURE_HANDLER_SIZE ARCHITECTURE_IDT_EXCEPTION_SIZE + \ + ARCHITECTURE_IDT_IRQ_SIZE + \ + ARCHITECTURE_IDT_IPI_SIZE + \ + ARCHITECTURE_IDT_SYSCALL_SIZE + +/* + * this macro defines the size of a thread's pile i.e the stack used whenever + * a privilege change occurs. + */ + +#define ARCHITECTURE_HANDLER_PILE_SIZE ___kaneton$pagesz + +/* + * this macro defines the size of the KIS - Kernel Interrupt Stack. this is + * the stack which is used, within the kernel environment, for treating + * interrupts. + */ + +#define ARCHITECTURE_HANDLER_KIS_SIZE 2 * ___kaneton$pagesz + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../handler.c + */ + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/architecture/ia32/educational/include/idt.h b/kaneton/machine/architecture/ia32/educational/include/idt.h new file mode 100644 index 0000000..f46f938 --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/include/idt.h @@ -0,0 +1,121 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...hitecture/ia32/educational/include/idt.h + * + * created renaud voltz [fri feb 10 16:36:20 2006] + * updated julien quintard [mon apr 11 13:45:20 2011] + */ + +#ifndef ARCHITECTURE_IDT_H +#define ARCHITECTURE_IDT_H 1 + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * these macros define the base entry and the number of entries for the + * several types of gate: IRQ, exception, IPI or syscall. + * + * note that 200 syscalls could be set up but the kernel limits itself + * to ten which is enough for a microkernel. + */ + +#define ARCHITECTURE_IDT_EXCEPTION_BASE 0 +#define ARCHITECTURE_IDT_EXCEPTION_SIZE 32 + +#define ARCHITECTURE_IDT_IRQ_BASE 32 +#define ARCHITECTURE_IDT_IRQ_SIZE 16 + +#define ARCHITECTURE_IDT_IPI_BASE 48 +#define ARCHITECTURE_IDT_IPI_SIZE 8 + +#define ARCHITECTURE_IDT_SYSCALL_BASE 56 +#define ARCHITECTURE_IDT_SYSCALL_SIZE 10 + +/* + * these macro define some of the exception handler sources. + */ + +#define ARCHITECTURE_IDT_EXCEPTION_DE \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 0 +#define ARCHITECTURE_IDT_EXCEPTION_DB \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 1 +#define ARCHITECTURE_IDT_EXCEPTION_BP \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 3 +#define ARCHITECTURE_IDT_EXCEPTION_OF \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 4 +#define ARCHITECTURE_IDT_EXCEPTION_BR \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 5 +#define ARCHITECTURE_IDT_EXCEPTION_UD \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 6 +#define ARCHITECTURE_IDT_EXCEPTION_NM \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 7 +#define ARCHITECTURE_IDT_EXCEPTION_DF \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 8 +#define ARCHITECTURE_IDT_EXCEPTION_TS \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 10 +#define ARCHITECTURE_IDT_EXCEPTION_NP \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 11 +#define ARCHITECTURE_IDT_EXCEPTION_SS \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 12 +#define ARCHITECTURE_IDT_EXCEPTION_GP \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 13 +#define ARCHITECTURE_IDT_EXCEPTION_PF \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 14 +#define ARCHITECTURE_IDT_EXCEPTION_MF \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 16 +#define ARCHITECTURE_IDT_EXCEPTION_AC \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 17 +#define ARCHITECTURE_IDT_EXCEPTION_MC \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 18 +#define ARCHITECTURE_IDT_EXCEPTION_XM \ + ARCHITECTURE_IDT_EXCEPTION_BASE + 19 + +/* + * these macro define some of the IRQ handler sources. + */ + +#define ARCHITECTURE_IDT_IRQ_PIT \ + ARCHITECTURE_IDT_IRQ_BASE + 0 +#define ARCHITECTURE_IDT_IRQ_KEYBOARD \ + ARCHITECTURE_IDT_IRQ_BASE + 1 +#define ARCHITECTURE_IDT_IRQ_CASCADE \ + ARCHITECTURE_IDT_IRQ_BASE + 2 +#define ARCHITECTURE_IDT_IRQ_COM2 \ + ARCHITECTURE_IDT_IRQ_BASE + 3 +#define ARCHITECTURE_IDT_IRQ_COM1 \ + ARCHITECTURE_IDT_IRQ_BASE + 4 +#define ARCHITECTURE_IDT_IRQ_FLOPPY \ + ARCHITECTURE_IDT_IRQ_BASE + 6 +#define ARCHITECTURE_IDT_IRQ_SPURIOUS \ + ARCHITECTURE_IDT_IRQ_BASE + 7 +#define ARCHITECTURE_IDT_IRQ_RTC \ + ARCHITECTURE_IDT_IRQ_BASE + 8 +#define ARCHITECTURE_IDT_IRQ_ATA1 \ + ARCHITECTURE_IDT_IRQ_BASE + 14 +#define ARCHITECTURE_IDT_IRQ_ATA2 \ + ARCHITECTURE_IDT_IRQ_BASE + 15 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../idt.c + */ + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/architecture/ia32/educational/include/io.h b/kaneton/machine/architecture/ia32/educational/include/io.h new file mode 100644 index 0000000..fdac37c --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/include/io.h @@ -0,0 +1,86 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...chitecture/ia32/educational/include/io.h + * + * created julien quintard [fri jan 7 20:00:03 2011] + * updated julien quintard [sun jan 9 22:46:17 2011] + */ + +#ifndef ARCHITECTURE_IO_H +#define ARCHITECTURE_IO_H 1 + +/* + * ---------- macro-functions ------------------------------------------------- + */ + +/* + * these macro-functions alias the assembly macro-functions in a more + * generic way such that platforms can use them for instance. + */ + +#define ARCHITECTURE_IO_IN_8 ARCHITECTURE_INB +#define ARCHITECTURE_IO_IN_16 ARCHITECTURE_INW +#define ARCHITECTURE_IO_IN_32 ARCHITECTURE_INL +#define ARCHITECTURE_IO_OUT_8 ARCHITECTURE_OUTB +#define ARCHITECTURE_IO_OUT_16 ARCHITECTURE_OUTW +#define ARCHITECTURE_IO_OUT_32 ARCHITECTURE_OUTL + +/* + * this macro defines the number of bits in an I/O bitmap. + */ + +#define ARCHITECTURE_IO_MAP_BITS 65536 + +/* + * this macro defines the size of an I/O bitmap, in bytes. + */ + +#define ARCHITECTURE_IO_MAP_SIZE ARCHITECTURE_IO_MAP_BITS / 8 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../io.c + */ + +/* + * ../io.c + */ + +t_error architecture_io_clear(i_task task); + +t_error architecture_io_set(t_uint8* map, + t_uint32 port, + t_uint32 value); + +t_error architecture_io_get(t_uint8* map, + t_uint32 port, + t_uint32* value); + +t_error architecture_io_grant(i_task task, + t_uint32 port, + t_uint8 width); + +t_error architecture_io_deny(i_task task, + t_uint32 port, + t_uint8 width); + +t_error architecture_io_reset(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/architecture/ia32/educational/include/linker.h b/kaneton/machine/architecture/ia32/educational/include/linker.h new file mode 100644 index 0000000..8f3debf --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/include/linker.h @@ -0,0 +1,52 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...ecture/ia32/educational/include/linker.h + * + * created julien quintard [sat oct 16 13:54:01 2010] + * updated julien quintard [mon jan 10 06:32:14 2011] + */ + +#ifndef ARCHITECTURE_LINKER_H +#define ARCHITECTURE_LINKER_H 1 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function is used to locate the memory address of a + * linker-specific symbol. + */ + +#define ARCHITECTURE_LINKER_SYMBOL(_symbol_) \ + ((t_vaddr)&(_symbol_)) + +/* + * this macro-function defines an attribute which is to be attached to + * definitions in order to place them at a precise location in the kernel + * binary image. + * + * this is used to place the handlers' code and data all together. indeed, + * let us recall that in the ia32/educational implementation, every interrupt + * being a system call, an IRQ, etc. triggers a handler shell which switches + * to the kernel address space. then, in the kernel environment, the interrupt + * is handled before returning to the task address space. + * + * this scheme has been adopted in order to avoid mapping the whole kernel + * in every task since these mappings must be constantly adjusted whenever + * the kernel modifies its page directory/table structure i.e + * allocates/deallocates memory. therefore, kaneton relies on small static + * region of memory which is mapped in every address space and contains the + * interrupt handlers' code and data, all that is necessary to switch to + * the kernel address space in order to treat the interrupt. + */ + +#define ARCHITECTURE_LINKER_LOCATION(_section_) \ + __attribute__ ((section(_section_))) + +#endif diff --git a/kaneton/machine/architecture/ia32/educational/include/paging.h b/kaneton/machine/architecture/ia32/educational/include/paging.h new file mode 100644 index 0000000..73eb8e2 --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/include/paging.h @@ -0,0 +1,83 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...ecture/ia32/educational/include/paging.h + * + * created julien quintard [fri feb 11 03:04:40 2005] + * updated julien quintard [mon apr 11 13:48:17 2011] + */ + +#ifndef ARCHITECTURE_PAGING_H +#define ARCHITECTURE_PAGING_H 1 + +/* + * ---------- macro-functions ------------------------------------------------- + */ + +/* + * computes a virtual address according to the given directory and table + * entries. + */ + +#define ARCHITECTURE_PAGING_ADDRESS(_pdei_, _ptei_) \ + (t_vaddr)(((_pdei_) << 22) | ((_ptei_) << 12)) + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +#include + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../paging.c + */ + +/* + * ../paging.c + */ + +t_error architecture_paging_pdbr(t_paddr pd, + t_flags flags, + at_cr3* pdbr); + +t_error architecture_paging_map(i_as id, + i_segment segment, + t_paddr offset, + t_options options, + t_vaddr address, + t_vsize size); + +t_error architecture_paging_unmap(i_as id, + t_vaddr address, + t_vsize size); + +t_error architecture_paging_read(i_segment id, + t_paddr offset, + void* buffer, + t_psize size); + +t_error architecture_paging_write(i_segment id, + t_paddr offset, + const void* buffer, + t_psize size); + +t_error architecture_paging_copy(i_region dst, + t_paddr to, + i_region src, + t_paddr from, + t_psize size); + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/architecture/ia32/educational/include/pd.h b/kaneton/machine/architecture/ia32/educational/include/pd.h new file mode 100644 index 0000000..83f8986 --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/include/pd.h @@ -0,0 +1,49 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/Down...chitecture/ia32/educational/include/pd.h + * + * created julien quintard [mon jan 10 09:05:19 2011] + * updated julien quintard [mon apr 11 13:20:03 2011] + */ + +#ifndef ARCHITECTURE_PD_H +#define ARCHITECTURE_PD_H 1 + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * this value defines the page directory entry which acts as the mirror + * entry i.e the entry referencing the page directory itself. + */ + +#define ARCHITECTURE_PD_MIRROR 1023 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../pd.c + */ + +/* + * ../pd.c + */ + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/architecture/ia32/educational/include/pmode.h b/kaneton/machine/architecture/ia32/educational/include/pmode.h new file mode 100644 index 0000000..5a2b8dd --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/include/pmode.h @@ -0,0 +1,39 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...tecture/ia32/educational/include/pmode.h + * + * created julien quintard [fri feb 11 03:04:40 2005] + * updated julien quintard [sat jan 8 19:15:41 2011] + */ + +#ifndef ARCHITECTURE_PMODE_H +#define ARCHITECTURE_PMODE_H 1 + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../pmode.c + */ + +/* + * ../pmode.c + */ + +t_error architecture_pmode_enable(void); + +t_error architecture_pmode_setup(void); + +t_error architecture_pmode_registers(t_uint16 code, + t_uint16 data); + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/architecture/ia32/educational/include/privilege.h b/kaneton/machine/architecture/ia32/educational/include/privilege.h new file mode 100644 index 0000000..2c74f22 --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/include/privilege.h @@ -0,0 +1,45 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license + * + * file /home/mycure/kane...ure/ia32/educational/include/privilege.h + * + * created julien quintard [fri jan 7 11:50:11 2011] + * updated julien quintard [sat jan 15 17:52:25 2011] + */ + +#ifndef ARCHITECTURE_PRIVILEGE_H +#define ARCHITECTURE_PRIVILEGE_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * the hardware privilege levels. + */ + +#define ARCHITECTURE_PRIVILEGE_RING0 0 +#define ARCHITECTURE_PRIVILEGE_RING1 1 +#define ARCHITECTURE_PRIVILEGE_RING2 2 +#define ARCHITECTURE_PRIVILEGE_RING3 3 + +/* + * the system privilege levels for the different task classes. + */ + +#define ARCHITECTURE_PRIVILEGE_KERNEL ARCHITECTURE_PRIVILEGE_RING0 +#define ARCHITECTURE_PRIVILEGE_DRIVER ARCHITECTURE_PRIVILEGE_RING1 +#define ARCHITECTURE_PRIVILEGE_SERVICE ARCHITECTURE_PRIVILEGE_RING2 +#define ARCHITECTURE_PRIVILEGE_GUEST ARCHITECTURE_PRIVILEGE_RING3 + +#endif diff --git a/kaneton/machine/architecture/ia32/educational/include/pt.h b/kaneton/machine/architecture/ia32/educational/include/pt.h new file mode 100644 index 0000000..cb2e15c --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/include/pt.h @@ -0,0 +1,48 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/Down...chitecture/ia32/educational/include/pt.h + * + * created julien quintard [mon jan 10 09:31:37 2011] + * updated julien quintard [mon apr 11 13:21:04 2011] + */ + +#ifndef ARCHITECTURE_PT_H +#define ARCHITECTURE_PT_H 1 + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * defines the number entries composing a page table. + */ + +#define ARCHITECTURE_PT_SIZE 1024 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../pt.c + */ + +/* + * ../pt.c + */ + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/architecture/ia32/educational/include/register.h b/kaneton/machine/architecture/ia32/educational/include/register.h new file mode 100644 index 0000000..cff18dc --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/include/register.h @@ -0,0 +1,89 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...ture/ia32/educational/include/register.h + * + * created julien quintard [wed jan 12 03:44:59 2011] + * updated julien quintard [sun jan 16 12:19:46 2011] + */ + +#ifndef ARCHITECTURE_REGISTER_H +#define ARCHITECTURE_REGISTER_H + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * the PCD - Page-level Cache disable and PCE - Page-level Cache Enable + * control whether caching is activated. + */ + +#define ARCHITECTURE_REGISTER_CR3_PCD (1 << 4) +#define ARCHITECTURE_REGISTER_CR3_PCE (0 << 4) + +/* + * these macro defines whether the caching mechanism is PWT - Page-level + * Write Through or PWB - Page-level Write Back. + */ + +#define ARCHITECTURE_REGISTER_CR3_PWT (1 << 3) +#define ARCHITECTURE_REGISTER_CR3_PWB (0 << 3) + +/* + * this flag enables the maskable interrupts to be delivered. + */ + +#define ARCHITECTURE_REGISTER_EFLAGS_IF (1 << 9) + +/* + * the bit 1 of the eflags must always be set. + */ + +#define ARCHITECTURE_REGISTER_EFLAGS_01 (1 << 1) + +/* + * ---------- macro-functions ------------------------------------------------- + */ + +/* + * this macro-function extracts the page directory address from a CR3 + * register. + */ + +#define ARCHITECTURE_REGISTER_CR3_ADDRESS_GET(_cr3_) \ + (_cr3_) & 0xfffff000 + +/* + * this macro-function sets the IOPL - I/O Privilege Level in the + * EFLAGS register. + */ + +#define ARCHITECTURE_REGISTER_EFLAGS_IOPL_SET(_privilege_) \ + (((_privilege_) & 0x3 )<< 12) + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * these types represent the CR registers. + */ + +typedef t_uint32 at_cr0; +typedef t_uint32 at_cr1; +typedef t_uint32 at_cr2; +typedef t_uint32 at_cr3; +typedef t_uint32 at_cr4; + +#endif diff --git a/kaneton/machine/architecture/ia32/educational/include/segmentation.h b/kaneton/machine/architecture/ia32/educational/include/segmentation.h new file mode 100644 index 0000000..cf827a0 --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/include/segmentation.h @@ -0,0 +1,34 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane.../ia32/educational/include/segmentation.h + * + * created julien quintard [fri jan 14 13:54:50 2011] + * updated julien quintard [fri jan 14 13:55:15 2011] + */ + +#ifndef ARCHITECTURE_SEGMENTATION_H +#define ARCHITECTURE_SEGMENTATION_H + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../segmentation.c + */ + +/* + * ../segmentation.c + */ + +t_error architecture_segmentation_setup(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/architecture/ia32/educational/include/tlb.h b/kaneton/machine/architecture/ia32/educational/include/tlb.h new file mode 100644 index 0000000..8845aa8 --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/include/tlb.h @@ -0,0 +1,36 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...hitecture/ia32/educational/include/tlb.h + * + * created julien quintard [sat jan 8 16:44:31 2011] + * updated julien quintard [sat jan 8 16:44:59 2011] + */ + +#ifndef ARCHITECTURE_TLB_H +#define ARCHITECTURE_TLB_H 1 + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../tlb.c + */ + +/* + * ../tlb.c + */ + +t_error architecture_tlb_invalidate(t_vaddr address); + +t_error architecture_tlb_flush(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/architecture/ia32/educational/include/tss.h b/kaneton/machine/architecture/ia32/educational/include/tss.h new file mode 100644 index 0000000..71ee6c5 --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/include/tss.h @@ -0,0 +1,126 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...hitecture/ia32/educational/include/tss.h + * + * created renaud voltz [mon apr 10 00:50:33 2006] + * updated julien quintard [sat jan 29 10:53:34 2011] + */ + +#ifndef ARCHITECTURE_TSS_H +#define ARCHITECTURE_TSS_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * this macro defines the number of pages required to contain the system's + * TSS. + */ + +#define ARCHITECTURE_TSS_SIZE 3 * ___kaneton$pagesz + +/* + * the two following macro can be used to ignore the SS0 and ESP0 TSS + * attributes. these are particularly useful when context switching to a + * ring0 thread since there is no need to specify a pile through SS0 and ESP0. + */ + +#define ARCHITECTURE_TSS_SS0_NULL 0x0 +#define ARCHITECTURE_TSS_ESP0_NULL 0x0 + +/* + * this macro defines the location, i.e offset, of the IO map within the TSS. + */ + +#define ARCHITECTURE_TSS_IO 0x68 + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * the tss structure. + */ + +typedef struct +{ + t_uint16 prev; + t_uint16 prev_align; + t_uint32 esp0; + t_uint16 ss0; + t_uint16 ss0_align; + t_uint32 esp1; + t_uint16 ss1; + t_uint16 ss1_align; + t_uint32 esp2; + t_uint16 ss2; + t_uint16 ss2_align; + t_uint32 cr3; + t_uint32 eip; + t_uint32 eflags; + t_uint32 eax; + t_uint32 ecx; + t_uint32 edx; + t_uint32 ebx; + t_uint32 esp; + t_uint32 ebp; + t_uint32 esi; + t_uint32 edi; + t_uint16 es; + t_uint16 es_align; + t_uint16 cs; + t_uint16 cs_align; + t_uint16 ss; + t_uint16 ss_align; + t_uint16 ds; + t_uint16 ds_align; + t_uint16 fs; + t_uint16 fs_align; + t_uint16 gs; + t_uint16 gs_align; + t_uint16 ldt; + t_uint16 ldt_align; + t_uint16 trap; + t_uint16 io; + t_uint8 io_bitmap[8192]; + t_uint8 io_end; +} __attribute__ ((packed)) as_tss; + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../tss.c + */ + +/* + * ../tss.c + */ + +t_error architecture_tss_build(t_vaddr address, + as_tss** tss); + +t_error architecture_tss_update(as_tss* tss, + t_uint16 ss0, + t_uint32 esp0, + t_uint32 io); + +t_error architecture_tss_activate(as_tss* tss); + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/architecture/ia32/educational/include/types.h b/kaneton/machine/architecture/ia32/educational/include/types.h new file mode 100644 index 0000000..b3e2d8d --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/include/types.h @@ -0,0 +1,53 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...tecture/ia32/educational/include/types.h + * + * created julien quintard [wed jun 6 19:07:02 2007] + * updated julien quintard [fri dec 17 22:22:25 2010] + */ + +#ifndef ARCHITECTURE_TYPES_H +#define ARCHITECTURE_TYPES_H 1 + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * fundamental types. + */ + +typedef unsigned char t_uint8; +typedef signed char t_sint8; +typedef unsigned short t_uint16; +typedef signed short t_sint16; +typedef unsigned int t_uint32; +typedef signed int t_sint32; +typedef unsigned long long int t_uint64; +typedef signed long long int t_sint64; + +typedef t_uint8 t_reg8; +typedef t_uint16 t_reg16; +typedef t_uint32 t_reg32; +typedef t_uint64 t_reg64; + +/* + * the physical address and size types. + */ + +typedef t_uint32 t_paddr; +typedef t_uint32 t_psize; + +/* + * the virtual address and size types. + */ + +typedef t_uint32 t_vaddr; +typedef t_uint32 t_vsize; + +#endif diff --git a/kaneton/machine/architecture/ia32/educational/io.c b/kaneton/machine/architecture/ia32/educational/io.c new file mode 100644 index 0000000..04bfd2c --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/io.c @@ -0,0 +1,297 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...chine/architecture/ia32/educational/io.c + * + * created julien quintard [sun jan 9 22:11:18 2011] + * updated julien quintard [sun jan 30 20:45:26 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file contains several functions for manipulating I/O bitmaps and + * accesses. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- exports --------------------------------------------------------- + */ + +/* + * the thread manager. + */ + +extern m_thread _thread; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function clears the given task's I/O permission bitmap. + * + * note that a bit set means that access is denied. therefore, this + * function sets all the bits to one. + * + * steps: + * + * 1) retrieve the task object. + * 2) clear the bitmap. + */ + +t_error architecture_io_clear(i_task task) +{ + o_task* o; + + /* + * 1) + */ + + if (task_get(task, &o) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + memset(&o->machine.io.map, 0xFF, sizeof (o->machine.io.map)); + + MACHINE_LEAVE(); +} + +/* + * this function modifies a bit in a map. + */ + +t_error architecture_io_set(t_uint8* map, + t_uint32 port, + t_uint32 value) +{ + if (value) + map[port / 8] |= (1 << (port % 8)); + else + map[port / 8] &= ~(1 << (port % 8)); + + MACHINE_LEAVE(); +} + +/* + * this function returns the value of a given bit in the map. + */ + +t_error architecture_io_get(t_uint8* map, + t_uint32 port, + t_uint32* value) +{ + *value = map[port / 8] & (1 << (port % 8)); + + MACHINE_LEAVE(); +} + +/* + * this function grants access to the given task on some I/O ports. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the task object. + * 2) activate the access to the given I/O ports, by setting the map's bit + * to zero. + * 3) retrieve the currently scheduled task's identifier. + * 4) retrieve the system's TSS structure. + * 5) update the task's current I/O. + * A) if the task is currently running, the CPU needs to be informed + * of the modified permissions by updating the TSS' I/O map. + * B) otherwise, mark the task's hardware I/O map as needing to be + * re-actualized. + */ + +t_error architecture_io_grant(i_task task, + t_uint32 port, + t_uint8 width) +{ + i_task current; + as_tss* tss; + o_task* o; + t_uint8 i; + + /* + * 0) + */ + + if ((port + width) >= ARCHITECTURE_IO_MAP_BITS) + MACHINE_ESCAPE("out-of-bound I/O port"); + + /* + * 1) + */ + + if (task_get(task, &o) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + for (i = 0; i < width; i++) + { + if (architecture_io_set(o->machine.io.map, + port + i, + 0) != ERROR_OK) + MACHINE_ESCAPE("unable to set the I/O bitmap"); + } + + /* + * 3) + */ + + if (task_current(¤t) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the current task's identifier"); + + /* + * 4) + */ + + tss = (as_tss*)_thread.machine.tss; + + /* + * 5) + */ + + if (current == task) + { + /* + * A) + */ + + memcpy(((t_uint8*)tss) + tss->io, + &o->machine.io.map, + sizeof (o->machine.io.map)); + } + else + { + /* + * B) + */ + + o->machine.io.flush = BOOLEAN_TRUE; + } + + MACHINE_LEAVE(); +} + +/* + * this function denies access to the given task on some I/O ports. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the task object. + * 2) deactivate the access to the given I/O ports, by setting the map's bit + * to one. + * 3) retrieve the currently scheduled task's identifier. + * 4) retrieve the system's TSS structure. + * 5) update the task's current I/O. + * A) if the task is currently running, the CPU needs to be informed + * of the modified permissions by updating the TSS' I/O map. + * B) otherwise, mark the task's hardware I/O map as needing to be + * re-actualized. + */ + +t_error architecture_io_deny(i_task task, + t_uint32 port, + t_uint8 width) +{ + i_task current; + as_tss* tss; + o_task* o; + t_uint8 i; + + /* + * 0) + */ + + if ((port + width) >= ARCHITECTURE_IO_MAP_BITS) + MACHINE_ESCAPE("out-of-bound I/O port"); + + /* + * 1) + */ + + if (task_get(task, &o) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + for (i = 0; i < width; i++) + { + if (architecture_io_set(o->machine.io.map, + port + i, + 1) != ERROR_OK) + MACHINE_ESCAPE("unable to set the I/O bitmap"); + } + + /* + * 3) + */ + + if (task_current(¤t) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the current task's identifier"); + + /* + * 4) + */ + + tss = (as_tss*)_thread.machine.tss; + + /* + * 5) + */ + + if (current == task) + { + /* + * A) + */ + + memcpy(((t_uint8*)tss) + tss->io, + &o->machine.io.map, + sizeof (o->machine.io.map)); + } + else + { + /* + * B) + */ + + o->machine.io.flush = BOOLEAN_TRUE; + } + + MACHINE_LEAVE(); +} + +/* + * this function sets the current I/O PL - I/O Privilege to zero. + */ + +t_error architecture_io_reset(void) +{ + asm volatile("pushf\n\t" + "andl $0xFFFFCFFF, %ss:(%esp)\n\t" + "popf"); + + MACHINE_LEAVE(); +} diff --git a/kaneton/machine/architecture/ia32/educational/paging.c b/kaneton/machine/architecture/ia32/educational/paging.c new file mode 100644 index 0000000..bf393bd --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/paging.c @@ -0,0 +1,545 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...e/architecture/ia32/educational/paging.c + * + * created matthieu bucchianeri [tue dec 20 13:45:05 2005] + * updated julien quintard [tue apr 12 07:34:31 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file provides basic paging functionalities. + * + * note that the ia32/educational implementation makes use of the mirroring + * technique. this technique is used to prevent the kernel from looping + * infinitely whenever trying to map a page table for instance. indeed, + * whenever a page table must be mapped, a virtual memory area is picked. + * this memory area must be mapped by creating a mapping in the page + * directory/table hierarchical structure. in order to do so, the kernel page + * directory and the kernel page table referencing the given area must + * also be mapped. should not this be the case, the referencing page table, + * for example, should be mapped by creating a mapping etc. there is an + * obvious infinite loop occuring here where every page table which has + * to be mapped implies another page table to reference it, hence to be + * mapped as well. + * + * in order to prevent this infinite loop, the kernel relies on the + * mirroring technique. this mechanism consists in setting up a kernel + * page directory entry as acting as a special loopback. for example, the + * last page directory entry, i.e pd[1023], will not reference another page + * table as it is the case for the other page directory entries but the + * page directory itself. this way, the page directory, whenever the + * mirroring page directory entry is used, is considered by the microprocessor + * as page table and its entries as page tables entries. + * + * the following schema depicts this organization: + * + * pd @ 0x011e2000 + * pde 0 references 0x011e3000 + * pt @ 0x011e3000 + * pte 0 not present + * pte 1 references 0x00001000 + * pte 2 references 0x00002000 + * pte 3 references 0x00003000 + * pte 4 references 0x00004000 + * ... + * pde 2 not present + * pde 3 not present + * pde 4 references 0x011e4000 + * pt @ 0x011e4000 + * pte 0 references 0x01000000 + * pte 1 references 0x01001000 + * pte 2 references 0x01002000 + * ... + * pde 1023 references 0x011e2000 (the page directory itself) + * pt @ 0x011e2000 (the page directory acts as a page table) + * pte 0 references 0x011e3000 (the first page table: pde 0) + * pte 4 references 0x011e4000 (the second page table: pde 1) + * pte 1023 references 0x011e2000 (the page directory: pde 1023) + * + * this technique---assuming the kernel page directory is mapped, but this is + * quite obvious---enables the kernel to modify any page directory/table + * without mapping any intermediate page table. indeed, the kernel only needs + * to reference the page table it wishes to modify through its mirrored + * page directory entry. + * + * this technique implies that the last page directory entry is reserved + * for the mirroring mechanism. therefore, the last 4MB of virtual memory + * must be locked for that purpose meaning that 4MB of memory are wasted. + * + * finally, the reader should have understood that the mirroring mechanism + * only applies to the kernel tables. indeed, other address spaces' + * page directories and tables can be mapped normally since, should + * a kernel table need to be mapped to access another non-kernel table, it + * will be through the mirroring mechanism. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the kernel manager. + */ + +extern m_kernel _kernel; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function generates the CR3 register's content so that to be + * overwritten, hence referencing another page directory structure. + * + * note that the CR3 register is also referred to as the PDBR - Page Directory + * Base Register as it contains the address of the page directory in use. + * + * steps: + * + * 0) verify the arguments. + * 1) generate the CR3 register's content. + */ + +t_error architecture_paging_pdbr(t_paddr pd, + t_flags flags, + at_cr3* pdbr) +{ + /* + * 0) + */ + + if (pdbr == NULL) + MACHINE_ESCAPE("the 'pdbr' argument is null"); + + /* + * 1) + */ + + *pdbr = (pd & 0xfffff000) | flags; + + MACHINE_LEAVE(); +} + +/* + * this function maps a portion of the given segment to the given virtual + * address. + */ + +t_error architecture_paging_map(i_as id, + i_segment segment, + t_paddr offset, + t_options options, + t_vaddr address, + t_vsize size) +{ + /* FIXME[code to complete] */ + + MACHINE_LEAVE(); +} + +/* + * this function unmaps the mappings associated with the given address and + * size. + */ + +t_error architecture_paging_unmap(i_as id, + t_vaddr address, + t_vsize size) +{ + /* FIXME[code to complete] */ + + MACHINE_LEAVE(); +} + +/* + * this function reads data from a given segment by temporarily mapping + * the necessary pages. + * + * note that this function supports non-aligned addresses and sizes. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the segment object. + * 2) compute the aligned segment offset along with the non-aligned shift. + * 3) compute the last aligned page boundary. + * 4) reserve a region for the involved pages. + * 5) retrieve the region object. + * 6) copy data from the mapped region into the given buffer. + * 7) release the region. + */ + +t_error architecture_paging_read(i_segment id, + t_paddr offset, + void* buffer, + t_psize size) +{ + t_paddr shift; + i_region region; + t_paddr end; + o_segment* o; + o_region* r; + + /* + * 0) + */ + + if (buffer == NULL) + MACHINE_ESCAPE("the 'buffer' argument is null"); + + /* + * 1) + */ + + if (segment_get(id, &o) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the segment object"); + + /* + * 2) + */ + + if (offset % ___kaneton$pagesz) + { + shift = offset - (offset & ~(___kaneton$pagesz - 1)); + offset = offset & ~(___kaneton$pagesz - 1); + } + else + { + shift = 0; + } + + /* + * 3) + */ + + end = offset + shift + size; + + if (end % ___kaneton$pagesz) + end = (end & ~(___kaneton$pagesz - 1)) + ___kaneton$pagesz; + + /* + * 4) + */ + + if (region_reserve(_kernel.as, + id, + offset, + REGION_OPTION_NONE, + 0x0, + end - offset, + ®ion) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve a region"); + + /* + * 5) + */ + + if (region_get(_kernel.as, region, &r) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the region object"); + + /* + * 6) + */ + + memcpy(buffer, (void*)r->address + shift, size); + + /* + * 7) + */ + + if (region_release(_kernel.as, region) != ERROR_OK) + MACHINE_ESCAPE("unable to release the region"); + + MACHINE_LEAVE(); +} + +/* + * this function writes data to a given segment by temporarily mapping + * the necessary pages. + * + * note that this function supports non-aligned addresses and sizes. + * + * steps: + * + * 0) verify the arguments. + * 1) retrieve the segment object. + * 2) compute the aligned segment offset along with the non-aligned shift. + * 3) compute the last aligned page boundary. + * 4) reserve a region for the involved pages. + * 5) retrieve the region object. + * 6) copy data from the given buffer to the mapped region. + * 7) release the region. + */ + +t_error architecture_paging_write(i_segment id, + t_paddr offset, + const void* buffer, + t_psize size) +{ + t_paddr shift; + i_region region; + t_paddr end; + o_segment* o; + o_region* r; + + /* + * 0) + */ + + if (buffer == NULL) + MACHINE_ESCAPE("the 'buffer' argument is null"); + + /* + * 1) + */ + + if (segment_get(id, &o) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the segment object"); + + /* + * 2) + */ + + if (offset % ___kaneton$pagesz) + { + shift = offset - (offset & ~(___kaneton$pagesz - 1)); + offset = offset & ~(___kaneton$pagesz - 1); + } + else + { + shift = 0; + } + + /* + * 3) + */ + + end = offset + shift + size; + + if (end % ___kaneton$pagesz) + end = (end & ~(___kaneton$pagesz - 1)) + ___kaneton$pagesz; + + /* + * 4) + */ + + if (region_reserve(_kernel.as, + id, + offset, + REGION_OPTION_NONE, + 0x0, + end - offset, + ®ion) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve a region"); + + /* + * 5) + */ + + if (region_get(_kernel.as, region, &r) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the region object"); + + /* + * 6) + */ + + memcpy((void*)r->address + shift, buffer, size); + + /* + * 7) + */ + + if (region_release(_kernel.as, region) != ERROR_OK) + MACHINE_ESCAPE("unable to release the region"); + + MACHINE_LEAVE(); +} + +/* + * this function copies data from a segment to another by temporarily + * mapping the necessary pages. + * + * note that this function supports non-aligned addresses and sizes. + * + * steps: + * + * 1) retrieve the segment objects. + * 2) compute the source aligned offset and non-aligned shift. + * 3) compute the source last aligned page boundary. + * 4) map the source pages through a region reservation. + * 5) retrieve the reserved region. + * 6) compute the destination aligned offset and non-aligned shift. + * 7) compute the destination last aligned page boundary. + * 8) map the destination pages through a region reservation. + * 9) retrieve the reserved region. + * 10) copy from the source mapped region to the destination mapped region. + * 11) release the reserved regions. + */ + +t_error architecture_paging_copy(i_region dst, + t_paddr to, + i_region src, + t_paddr from, + t_psize size) +{ + struct + { + struct + { + o_segment* object; + } segment; + struct + { + i_region id; + o_region* object; + } region; + t_paddr shift; + } source; + struct + { + struct + { + o_segment* object; + } segment; + struct + { + i_region id; + o_region* object; + } region; + t_paddr shift; + } destination; + t_paddr end; + + /* + * 1) + */ + + if (segment_get(dst, &destination.segment.object) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the segment object"); + + if (segment_get(src, &source.segment.object) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the segment object"); + + /* + * 2) + */ + + if (from % ___kaneton$pagesz) + { + source.shift = from - (from & ~(___kaneton$pagesz - 1)); + from = from & ~(___kaneton$pagesz - 1); + } + else + { + source.shift = 0; + } + + /* + * 3) + */ + + end = from + source.shift + size; + + if (end % ___kaneton$pagesz) + end = (end & ~(___kaneton$pagesz - 1)) + ___kaneton$pagesz; + + /* + * 4) + */ + + if (region_reserve(_kernel.as, + src, + from, + REGION_OPTION_NONE, + 0x0, + end - from, + &source.region.id) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve a region"); + + /* + * 5) + */ + + if (region_get(_kernel.as, + source.region.id, + &source.region.object) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the region object"); + + /* + * 6) + */ + + if (to % ___kaneton$pagesz) + { + destination.shift = to - (to & ~(___kaneton$pagesz - 1)); + to = to & ~(___kaneton$pagesz - 1); + } + else + { + destination.shift = 0; + } + + /* + * 7) + */ + + end = to + destination.shift + size; + + if (end % ___kaneton$pagesz) + end = (end & ~(___kaneton$pagesz - 1)) + ___kaneton$pagesz; + + /* + * 8) + */ + + if (region_reserve(_kernel.as, + dst, + to, + REGION_OPTION_NONE, + 0x0, + end - to, + &destination.region.id) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve a region"); + + /* + * 9) + */ + + if (region_get(_kernel.as, + destination.region.id, + &destination.region.object) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the region object"); + + /* + * 10) + */ + + memcpy((void*)destination.region.object->address + destination.shift, + (void*)source.region.object->address + source.shift, + size); + + /* + * 11) + */ + + if (region_release(_kernel.as, source.region.id) != ERROR_OK) + MACHINE_ESCAPE("unable to release the region"); + + if (region_release(_kernel.as, destination.region.id) != ERROR_OK) + MACHINE_ESCAPE("unable to release the region"); + + MACHINE_LEAVE(); +} diff --git a/kaneton/machine/architecture/ia32/educational/pd.c b/kaneton/machine/architecture/ia32/educational/pd.c new file mode 100644 index 0000000..bc86ceb --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/pd.c @@ -0,0 +1,30 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...chine/architecture/ia32/educational/pd.c + * + * created matthieu bucchianeri [tue dec 20 19:56:20 2005] + * updated julien quintard [mon apr 11 13:45:04 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file contains functions for managing PDs - Page Directories. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* FIXME[complete if necessary] */ diff --git a/kaneton/machine/architecture/ia32/educational/pmode.c b/kaneton/machine/architecture/ia32/educational/pmode.c new file mode 100644 index 0000000..45c1380 --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/pmode.c @@ -0,0 +1,127 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...ne/architecture/ia32/educational/pmode.c + * + * created julien quintard [sat jan 8 19:01:44 2011] + * updated julien quintard [sun jan 16 13:42:19 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file contains functions for managing the ia32 protected mode. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the init structure. + */ + +extern s_init* _init; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function enables the protected mode by setting the PE bit in CR0. + */ + +t_error architecture_pmode_enable(void) +{ + asm volatile("movl %%cr0, %%eax\n" + "orw $1, %%ax\n" + "movl %%eax, %%cr0\n" + : + : + : "%eax"); + + asm volatile("jmp 1f\n" + "1:" + ); + + MACHINE_LEAVE(); +} + +/* + * this function sets up the protected mode. + * + * steps: + * + * 1) build a GDT descriptor based on the GDT prepared by the boot loader. + * 2) import the descriptor in order to make it the current system's GDT. + * 3) enable the protected mode. + */ + +t_error architecture_pmode_setup(void) +{ + as_gdt gdt; + + /* + * 1) + */ + + gdt.table = (void*)_init->machine.gdt; + gdt.size = ARCHITECTURE_GDT_SIZE; + + /* + * 2) + */ + + if (architecture_gdt_build(_init->machine.gdt, + ARCHITECTURE_GDT_SIZE * sizeof (at_gdte), + &gdt) != ERROR_OK) + MACHINE_ESCAPE("unable to build the GDT"); + + if (architecture_gdt_import(&gdt) != ERROR_OK) + MACHINE_ESCAPE("unable to import the boot loader's GDT"); + + /* + * 3) + */ + + if (architecture_pmode_enable() != ERROR_OK) + MACHINE_ESCAPE("unable to enable the protected mode"); + + MACHINE_LEAVE(); +} + +/* + * this function install the given segment registers by updating + * the processor's segment registers. + */ + +t_error architecture_pmode_registers(t_uint16 code, + t_uint16 data) +{ + asm volatile("pushl %0\n\t" + "pushl $1f\n\t" + "lret\n\t" + "1:\n\t" + "movw %1, %%ax\n\t" + "movw %%ax, %%ds\n\t" + "movw %%ax, %%ss\n\t" + "movw %%ax, %%es\n\t" + "movw %%ax, %%fs\n\t" + "movw %%ax, %%gs\n\t" + : + : "g" (code), "g" (data) + : "memory", "%eax" + ); + + MACHINE_LEAVE(); +} diff --git a/kaneton/machine/architecture/ia32/educational/pt.c b/kaneton/machine/architecture/ia32/educational/pt.c new file mode 100644 index 0000000..c8b2a60 --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/pt.c @@ -0,0 +1,32 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...chine/architecture/ia32/educational/pt.c + * + * created matthieu bucchianeri [tue dec 20 19:56:48 2005] + * updated julien quintard [mon apr 11 13:45:11 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file contains functions for manipulating PTs - Page Tables. + * + * note that the whole file is extremely similar to pd.c. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* FIXME[complete if necessary] */ diff --git a/kaneton/machine/architecture/ia32/educational/segmentation.c b/kaneton/machine/architecture/ia32/educational/segmentation.c new file mode 100644 index 0000000..6002bdf --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/segmentation.c @@ -0,0 +1,183 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...itecture/ia32/educational/segmentation.c + * + * created julien quintard [fri jan 14 13:51:10 2011] + * updated julien quintard [sun jan 16 13:38:05 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file contains functionalities to managing the IA32 segmentation. + * + * note however that most of the functionalities are included in the pmode.c + * file. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the architecture manager. + */ + +extern am _architecture; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function sets up the IA32 segmentation by registering the code and + * data segments for the various task classes: kernel, driver, service and + * guest. + * + * steps: + * + * 1) register the kernel code and data segments in the GDT. + * 2) register the driver code and data segments in the GDT. + * 3) register the service code and data segments in the GDT. + * 4) register the guest code and data segments in the GDT. + * 5) build the kernel code and data segment selectors. + * 6) update the microprocessor's segment registers with the given + * segment selectors. + * 7) finally, save the kernel data selector as the system will need + * it whenever an interrupt occurs in order to switch to the kernel + * address space and trigger the interrupt handler. + */ + +t_error architecture_segmentation_setup(void) +{ + t_uint16 code; + t_uint16 data; + + /* + * 1) + */ + + if (architecture_gdt_insert(ARCHITECTURE_GDT_INDEX_KERNEL_CODE, + 0x0, + 0xffffffff, + ARCHITECTURE_GDTE_DPL_SET( + ARCHITECTURE_PRIVILEGE_KERNEL) | + ARCHITECTURE_GDTE_S | + ARCHITECTURE_GDTE_CODE) != ERROR_OK) + MACHINE_ESCAPE("unable to insert the kernel code GDT segment"); + + if (architecture_gdt_insert(ARCHITECTURE_GDT_INDEX_KERNEL_DATA, + 0x0, + 0xffffffff, + ARCHITECTURE_GDTE_DPL_SET( + ARCHITECTURE_PRIVILEGE_KERNEL) | + ARCHITECTURE_GDTE_S | + ARCHITECTURE_GDTE_DATA) != ERROR_OK) + MACHINE_ESCAPE("unable to insert the kernel data GDT segment"); + + /* + * 2) + */ + + if (architecture_gdt_insert(ARCHITECTURE_GDT_INDEX_DRIVER_CODE, + 0x0, + 0xffffffff, + ARCHITECTURE_GDTE_DPL_SET( + ARCHITECTURE_PRIVILEGE_DRIVER) | + ARCHITECTURE_GDTE_S | + ARCHITECTURE_GDTE_CODE) != ERROR_OK) + MACHINE_ESCAPE("unable to insert the driver code GDT segment"); + + if (architecture_gdt_insert(ARCHITECTURE_GDT_INDEX_DRIVER_DATA, + 0x0, + 0xffffffff, + ARCHITECTURE_GDTE_DPL_SET( + ARCHITECTURE_PRIVILEGE_DRIVER) | + ARCHITECTURE_GDTE_S | + ARCHITECTURE_GDTE_DATA) != ERROR_OK) + MACHINE_ESCAPE("unable to insert the driver data GDT segment"); + + /* + * 3) + */ + + if (architecture_gdt_insert(ARCHITECTURE_GDT_INDEX_SERVICE_CODE, + 0x0, + 0xffffffff, + ARCHITECTURE_GDTE_DPL_SET( + ARCHITECTURE_PRIVILEGE_SERVICE) | + ARCHITECTURE_GDTE_S | + ARCHITECTURE_GDTE_CODE) != ERROR_OK) + MACHINE_ESCAPE("unable to insert the service code GDT segment"); + + if (architecture_gdt_insert(ARCHITECTURE_GDT_INDEX_SERVICE_DATA, + 0x0, + 0xffffffff, + ARCHITECTURE_GDTE_DPL_SET( + ARCHITECTURE_PRIVILEGE_SERVICE) | + ARCHITECTURE_GDTE_S | + ARCHITECTURE_GDTE_DATA) != ERROR_OK) + MACHINE_ESCAPE("unable to insert the service data GDT segment"); + + /* + * 4) + */ + + if (architecture_gdt_insert(ARCHITECTURE_GDT_INDEX_GUEST_CODE, + 0x0, + 0xffffffff, + ARCHITECTURE_GDTE_DPL_SET( + ARCHITECTURE_PRIVILEGE_GUEST) | + ARCHITECTURE_GDTE_S | + ARCHITECTURE_GDTE_CODE) != ERROR_OK) + MACHINE_ESCAPE("unable to insert the guest code GDT segment"); + + if (architecture_gdt_insert(ARCHITECTURE_GDT_INDEX_GUEST_DATA, + 0x0, + 0xffffffff, + ARCHITECTURE_GDTE_DPL_SET( + ARCHITECTURE_PRIVILEGE_GUEST) | + ARCHITECTURE_GDTE_S | + ARCHITECTURE_GDTE_DATA) != ERROR_OK) + MACHINE_ESCAPE("unable to insert the guest data GDT segment"); + + /* + * 5) + */ + + if (architecture_gdt_selector(ARCHITECTURE_GDT_INDEX_KERNEL_CODE, + ARCHITECTURE_PRIVILEGE_KERNEL, + &code) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the kernel code segment selector"); + + if (architecture_gdt_selector(ARCHITECTURE_GDT_INDEX_KERNEL_DATA, + ARCHITECTURE_PRIVILEGE_KERNEL, + &data) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the kernel data segment selector"); + + /* + * 6) + */ + + if (architecture_pmode_registers(code, data) != ERROR_OK) + MACHINE_ESCAPE("unable to update the segment's registers"); + + /* + * 7) + */ + + _architecture.kernel.ds = data; + + MACHINE_LEAVE(); +} diff --git a/kaneton/machine/architecture/ia32/educational/tlb.c b/kaneton/machine/architecture/ia32/educational/tlb.c new file mode 100644 index 0000000..6ce7f4d --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/tlb.c @@ -0,0 +1,58 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...hine/architecture/ia32/educational/tlb.c + * + * created matthieu bucchianeri [tue dec 20 19:57:00 2005] + * updated julien quintard [sat feb 5 10:40:55 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file contains functions for managing the TLB - Translation + * Lookaside Buffer. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function invalidates a single address from the TLB. + */ + +t_error architecture_tlb_invalidate(t_vaddr address) +{ + asm volatile("invlpg (%0)" + : + : "r" (address) + : "memory"); + + MACHINE_LEAVE(); +} + +/* + * this function flushes the whole TLB's content. + */ + +t_error architecture_tlb_flush(void) +{ + asm volatile("movl %%cr3, %%eax\n\t" + "movl %%eax, %%cr3" + : + : + : "%eax", "memory"); + + MACHINE_LEAVE(); +} diff --git a/kaneton/machine/architecture/ia32/educational/tss.c b/kaneton/machine/architecture/ia32/educational/tss.c new file mode 100644 index 0000000..c9ff8c3 --- /dev/null +++ b/kaneton/machine/architecture/ia32/educational/tss.c @@ -0,0 +1,158 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...hine/architecture/ia32/educational/tss.c + * + * created renaud voltz [mon apr 10 01:01:34 2006] + * updated julien quintard [sat jan 29 10:55:16 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file contains functions for managing the TSS - Task State Segment. + * + * in order to understand why the ia32 architecture makes use of TSSs, + * one must first notice that event handlers---being for exceptions, IRQs, + * IPIs or syscalls---are always executed in kernel land. + * + * therefore, whenever an event occurs, for instance while a server is + * running, the ia32 architecture automatically performs a task switch, + * hence replacing the current CPL3 stack with the thread's CPL0 stack, + * commonly referred to as the thread's 'pile'. + * + * in order to perform this automatic stack switching, the CPU must know + * the location of the thread's CPL0 stack i.e pile among other things. + * + * a TSS is therefore used by the system in order to store the currently + * executing thread's informations, including the location of its pile. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function builds an initial tss. + * + * steps: + * + * 1) set the TSS. + * 2) initialize its memory. + */ + +t_error architecture_tss_build(t_vaddr address, + as_tss** tss) +{ + /* + * 1) + */ + + *tss = (as_tss*)address; + + /* + * 2) + */ + + memset(*tss, 0x0, sizeof (as_tss)); + + MACHINE_LEAVE(); +} + +/* + * this function updates the given TSS according to the given parameters. + * + * steps: + * + * 0) verify the arguments. + * 1) fill the TSS' attributes. + */ + +t_error architecture_tss_update(as_tss* tss, + t_uint16 ss0, + t_uint32 esp0, + t_uint32 io) +{ + /* + * 0) + */ + + if (tss == NULL) + MACHINE_ESCAPE("the 'tss' argument is null"); + + /* + * 1) + */ + + tss->ss0 = ss0; + tss->esp0 = esp0; + tss->io = io; + tss->io_end = 0xFF; + + MACHINE_LEAVE(); +} + +/* + * this function makes the given TSS the system's current one. + * + * steps: + * + * 0) verify the arguments. + * 1) add the TSS to the GDT. note that the GDT system entry for the TSS + * is supposed to store a linear address but since the GDT function + * only accepts physical addresses, a cast is performed. + * 2) build the TSS segment selector. + * 3) load the selector in the TR register. + */ + +t_error architecture_tss_activate(as_tss* tss) +{ + t_uint16 index; + t_uint16 selector; + + /* + * 0) + */ + + if (tss == NULL) + MACHINE_ESCAPE("the 'tss' argument is null"); + + /* + * 1) + */ + + if (architecture_gdt_reserve((t_paddr)tss, + sizeof (as_tss), + ARCHITECTURE_GDTE_DPL_SET( + ARCHITECTURE_PRIVILEGE_RING0) | + ARCHITECTURE_GDTE_TSS, + &index) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve a GDT entry"); + + /* + * 2) + */ + + if (architecture_gdt_selector(index, + ARCHITECTURE_PRIVILEGE_RING0, + &selector) != ERROR_OK) + MACHINE_ESCAPE("unable to build the TSS segment selector"); + + /* + * 3) + */ + + ARCHITECTURE_LTR(selector); + + MACHINE_LEAVE(); +} diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/Makefile b/kaneton/machine/glue/ibm-pc.ia32/educational/Makefile new file mode 100644 index 0000000..135024e --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/Makefile @@ -0,0 +1,100 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kane...ne/glue/ibm-pc.ia32/educational/Makefile +# +# created julien quintard [sun jun 10 15:47:23 2007] +# updated julien quintard [fri dec 17 20:33:46 2010] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/machine/glue + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers + +# +# ---------- variables -------------------------------------------------------- +# + +GLUE_C := as.c \ + clock.c \ + cpu.c \ + event.c \ + io.c \ + kernel.c \ + map.c \ + message.c \ + region.c \ + scheduler.c \ + segment.c \ + task.c \ + thread.c \ + timer.c + +GLUE_O := $(GLUE_C:.c=.o) + +GLUE_INCLUDE := $(_GLUE_INCLUDE_DIR_)/as.h \ + $(_GLUE_INCLUDE_DIR_)/clock.h \ + $(_GLUE_INCLUDE_DIR_)/cpu.h \ + $(_GLUE_INCLUDE_DIR_)/event.h \ + $(_GLUE_INCLUDE_DIR_)/io.h \ + $(_GLUE_INCLUDE_DIR_)/kernel.h \ + $(_GLUE_INCLUDE_DIR_)/map.h \ + $(_GLUE_INCLUDE_DIR_)/message.h \ + $(_GLUE_INCLUDE_DIR_)/region.h \ + $(_GLUE_INCLUDE_DIR_)/scheduler.h \ + $(_GLUE_INCLUDE_DIR_)/segment.h \ + $(_GLUE_INCLUDE_DIR_)/task.h \ + $(_GLUE_INCLUDE_DIR_)/thread.h \ + $(_GLUE_INCLUDE_DIR_)/timer.h + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: $(_GLUE_LO_) + +$(_GLUE_LO_): $(GLUE_O) + $(call env_remove,$(_GLUE_LO_),) + + $(call env_archive,$(_GLUE_LO_),$(GLUE_O),) + +clear: + $(call env_remove,$(GLUE_O),) + + $(call env_remove,$(_GLUE_LO_),) + + $(call env_purge,) + +prototypes: + $(call env_prototypes,$(GLUE_INCLUDE),) + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(GLUE_C),) + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/as.c b/kaneton/machine/glue/ibm-pc.ia32/educational/as.c new file mode 100644 index 0000000..2bbe576 --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/as.c @@ -0,0 +1,88 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...achine/glue/ibm-pc.ia32/educational/as.c + * + * created matthieu bucchianeri [sat jun 16 18:10:38 2007] + * updated julien quintard [sat feb 5 13:57:19 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file implements the glue for the address space manager. + * + * the glue particularly sets up the initial address space's state by + * building the IA32 page directory. note that glue_as_reserve() + * detects the kernel address space as this one must be prepared specially. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the kernel manager. + */ + +extern m_kernel _kernel; + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the address space dispatcher. + */ + +d_as glue_as_dispatch = + { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + glue_as_reserve, + NULL, + NULL, + NULL + }; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function initializes the given address space depending to the task, + * being either the kernel or one of the servers i.e a driver, service or + * guest. + */ + +t_error glue_as_reserve(i_task task, + i_as* as) +{ + if (*as == _kernel.as) + { + if (architecture_environment_kernel(*as) != ERROR_OK) + MACHINE_ESCAPE("unable to initialize the kernel's address space"); + } + else + { + if (architecture_environment_server(*as) != ERROR_OK) + MACHINE_ESCAPE("unable to initialize the server's address space"); + } + + MACHINE_LEAVE(); +} diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/clock.c b/kaneton/machine/glue/ibm-pc.ia32/educational/clock.c new file mode 100644 index 0000000..b55fffa --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/clock.c @@ -0,0 +1,141 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...ine/glue/ibm-pc.ia32/educational/clock.c + * + * created julien quintard [wed nov 24 19:11:47 2010] + * updated julien quintard [sat jan 15 06:10:00 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file implements the clock manager's glue. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the clock dispatcher. + */ + +d_clock glue_clock_dispatch = + { + NULL, + glue_clock_update, + glue_clock_current, + glue_clock_initialize, + glue_clock_clean + }; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function updates the clock with the given number of milliseconds. + * + * steps: + * + * 1) call the RTC update() function. + */ + +t_error glue_clock_update(t_uint32 millisecond) +{ + /* + * 1) + */ + + if (platform_rtc_update(millisecond) != ERROR_OK) + MACHINE_ESCAPE("unable to update the RTC"); + + MACHINE_LEAVE(); +} + +/* + * this function returns the current date/time through the clock structure. + * + * steps: + * + * 1) call the RTC to retrieve the hardware information related to the date + * and time. + * 2) fill the clock structure with the RTC. + */ + +t_error glue_clock_current(s_clock* clock) +{ + ps_rtc_state* rtc; + + /* + * 1) + */ + + if (platform_rtc_state(&rtc) != ERROR_OK) + MACHINE_ESCAPE("unable to load the RTC date/time structure"); + + /* + * 2) + */ + + clock->millisecond = rtc->millisecond; + clock->second = rtc->second; + clock->minute = rtc->minute; + clock->hour = rtc->hour; + + clock->day = rtc->day; + clock->month = rtc->month; + clock->year = rtc->century * 100 + rtc->year; + + MACHINE_LEAVE(); +} + +/* + * this function initializes the clock manager by setting up the + * platform's RTC. + * + * steps: + * + * 1) initialize the RTC. + */ + +t_error glue_clock_initialize(void) +{ + /* + * 1) + */ + + if (platform_rtc_initialize() != ERROR_OK) + MACHINE_ESCAPE("unable to initialize the real time clock"); + + MACHINE_LEAVE(); +} + +/* + * this function cleans the clock. + * + * 1) clean the RTC + */ + +t_error glue_clock_clean(void) +{ + /* + * 1) + */ + + if (platform_rtc_clean() != ERROR_OK) + MACHINE_ESCAPE("unable to clean the real time clock"); + + MACHINE_LEAVE(); +} diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/cpu.c b/kaneton/machine/glue/ibm-pc.ia32/educational/cpu.c new file mode 100644 index 0000000..6419aaf --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/cpu.c @@ -0,0 +1,74 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...chine/glue/ibm-pc.ia32/educational/cpu.c + * + * created matthieu bucchianeri [sat jul 29 18:04:01 2006] + * updated julien quintard [sun dec 19 16:42:48 2010] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * the file implements the CPU manager's glue. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the init structure provided by the boot loader. + */ + +extern s_init* _init; + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the CPU dispatcher. + */ + +d_cpu glue_cpu_dispatch = + { + NULL, + NULL, + glue_cpu_current, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL + }; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function returns the current CPU. + * + * since the ibm-pc.ia32/educational implementation assumes a single + * processor, the current CPU is always the BSP - Boot Strap Processor i.e + * the only processor. + */ + +t_error glue_cpu_current(i_cpu* cpu) +{ + *cpu = _init->bsp; + + MACHINE_LEAVE(); +} diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/event.c b/kaneton/machine/glue/ibm-pc.ia32/educational/event.c new file mode 100644 index 0000000..0d7e027 --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/event.c @@ -0,0 +1,45 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...ine/glue/ibm-pc.ia32/educational/event.c + * + * created renaud voltz [mon feb 13 01:05:52 2006] + * updated julien quintard [sat feb 5 12:30:16 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file implements the event manager's glue. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the event dispatcher. + */ + +d_event glue_event_dispatch = + { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL + }; diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/include/as.h b/kaneton/machine/glue/ibm-pc.ia32/educational/include/as.h new file mode 100644 index 0000000..7d89073 --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/include/as.h @@ -0,0 +1,90 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...lue/ibm-pc.ia32/educational/include/as.h + * + * created julien quintard [sun jun 3 23:54:56 2007] + * updated julien quintard [sat feb 5 15:44:46 2011] + */ + +#ifndef GLUE_AS_H +#define GLUE_AS_H 1 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function defines the address space dispatcher. + */ + +#define machine_include_as() \ + extern d_as glue_as_dispatch + +/* + * this macro-function dispatches the address space calls. + */ + +#define machine_call_as(_function_, _args_...) \ + ( \ + { \ + t_error _r_ = ERROR_OK; \ + \ + if (glue_as_dispatch.as_ ## _function_ != NULL) \ + _r_ = glue_as_dispatch.as_ ## _function_(_args_); \ + \ + _r_; \ + } \ + ) + +/* + * this macro-function includes data in the 'm_as' type. + */ + +#define machine_data_m_as() + +/* + * this macro-function includes data in the address space object 'o_as': + * the address space IA32-specific PD - Page directory's location. + */ + +#define machine_data_o_as() \ + struct \ + { \ + t_paddr pd; \ + } machine; + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../as.c + */ + +/* + * ../as.c + */ + +t_error glue_as_show(i_as id, + mt_margin margin); + +t_error glue_as_dump(void); + +t_error glue_as_reserve(i_task task, + i_as* as); + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/include/clock.h b/kaneton/machine/glue/ibm-pc.ia32/educational/include/clock.h new file mode 100644 index 0000000..2efdd40 --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/include/clock.h @@ -0,0 +1,80 @@ + +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane.../ibm-pc.ia32/educational/include/clock.h + * + * created julien quintard [wed jun 6 16:29:41 2007] + * updated julien quintard [sun dec 19 12:45:13 2010] + */ + +#ifndef GLUE_CLOCK_H +#define GLUE_CLOCK_H 1 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function defines the clock dispatcher. + */ + +#define machine_include_clock() \ + extern d_clock glue_clock_dispatch + +/* + * this macro-function dispatches the clock calls. + */ + +#define machine_call_clock(_function_, _args_...) \ + ( \ + { \ + t_error _r_ = ERROR_OK; \ + \ + if (glue_clock_dispatch.clock_ ## _function_ != NULL) \ + _r_ = glue_clock_dispatch.clock_ ## _function_(_args_); \ + \ + _r_; \ + } \ + ) + +/* + * this macro-function includes data in the 'm_clock' type. + */ + +#define machine_data_m_clock() + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../clock.c + */ + +/* + * ../clock.c + */ + +t_error glue_clock_update(t_uint32 millisecond); + +t_error glue_clock_current(s_clock* clock); + +t_error glue_clock_initialize(void); + +t_error glue_clock_clean(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/include/cpu.h b/kaneton/machine/glue/ibm-pc.ia32/educational/include/cpu.h new file mode 100644 index 0000000..1d0c066 --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/include/cpu.h @@ -0,0 +1,73 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...ue/ibm-pc.ia32/educational/include/cpu.h + * + * created julien quintard [mon jun 4 01:05:31 2007] + * updated julien quintard [sun dec 19 13:38:26 2010] + */ + +#ifndef GLUE_CPU_H +#define GLUE_CPU_H 1 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function defines the CPU dispatcher. + */ + +#define machine_include_cpu() \ + extern d_cpu glue_cpu_dispatch + +/* + * this macro-function dispatches the CPU calls. + */ + +#define machine_call_cpu(_function_, _args_...) \ + ( \ + { \ + t_error _r_ = ERROR_OK; \ + \ + if (glue_cpu_dispatch.cpu_ ## _function_ != NULL) \ + _r_ = glue_cpu_dispatch.cpu_ ## _function_(_args_); \ + \ + _r_; \ + } \ + ) + +/* + * this macro-function includes data in 'm_cpu'. + */ + +#define machine_data_m_cpu() + +/* + * this macro-function includes data in 'o_cpu'. + */ + +#define machine_data_o_cpu() + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../cpu.c + */ + +/* + * ../cpu.c + */ + +t_error glue_cpu_current(i_cpu* cpu); + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/include/event.h b/kaneton/machine/glue/ibm-pc.ia32/educational/include/event.h new file mode 100644 index 0000000..c5a47db --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/include/event.h @@ -0,0 +1,83 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane.../ibm-pc.ia32/educational/include/event.h + * + * created julien quintard [wed jun 6 16:15:26 2007] + * updated julien quintard [sat feb 5 13:30:56 2011] + */ + +#ifndef GLUE_EVENT_H +#define GLUE_EVENT_H 1 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function defines the event dispatcher. + */ + +#define machine_include_event() \ + extern d_event glue_event_dispatch + +/* + * this macro-function dispatches the CPU calls. + */ + +#define machine_call_event(_function_, _args_...) \ + ( \ + { \ + t_error _r_ = ERROR_OK; \ + \ + if (glue_event_dispatch.event_ ## _function_ != NULL) \ + _r_ = glue_event_dispatch.event_ ## _function_(_args_); \ + \ + _r_; \ + } \ + ) + +/* + * this macro-function includes data in 'm_event'. + */ + +#define machine_data_m_event() + +/* + * this macro-function includes data in 'o_event'. + */ + +#define machine_data_o_event() + +/* + * this macro-function includes data in 'o_event_message'. + */ + +#define machine_data_o_event_message() + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../event.c + */ + +/* + * ../event.c + */ + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/include/glue.h b/kaneton/machine/glue/ibm-pc.ia32/educational/include/glue.h new file mode 100644 index 0000000..53b6b4d --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/include/glue.h @@ -0,0 +1,44 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...e/ibm-pc.ia32/educational/include/glue.h + * + * created julien quintard [mon jun 11 05:57:15 2007] + * updated julien quintard [sun jan 30 13:19:50 2011] + */ + +#ifndef GLUE_GLUE_H +#define GLUE_GLUE_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include +#include + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/include/init.h b/kaneton/machine/glue/ibm-pc.ia32/educational/include/init.h new file mode 100644 index 0000000..5c77c91 --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/include/init.h @@ -0,0 +1,33 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...e/ibm-pc.ia32/educational/include/init.h + * + * created julien quintard [wed jun 6 16:16:48 2007] + * updated julien quintard [thu apr 7 14:55:37 2011] + */ + +#ifndef GLUE_INIT_H +#define GLUE_INIT_H 1 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function includes data in the 's_init' structure: the addresses + * of both the GDT - Global Descriptor Table and PD - Page Directory. + */ + +#define machine_data_s_init() \ + struct \ + { \ + t_paddr gdt; \ + t_paddr pd; \ + } machine; + +#endif diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/include/io.h b/kaneton/machine/glue/ibm-pc.ia32/educational/include/io.h new file mode 100644 index 0000000..a016c1f --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/include/io.h @@ -0,0 +1,85 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...lue/ibm-pc.ia32/educational/include/io.h + * + * created julien quintard [wed jun 6 16:19:54 2007] + * updated julien quintard [sun dec 19 13:41:25 2010] + */ + +#ifndef GLUE_IO_H +#define GLUE_IO_H 1 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function defines the I/O dispatcher. + */ + +#define machine_include_io() \ + extern d_io glue_io_dispatch + +/* + * this macro-function dispatches the I/O calls. + */ + +#define machine_call_io(_function_, _args_...) \ + ( \ + { \ + t_error _r_ = ERROR_OK; \ + \ + if (glue_io_dispatch.io_ ## _function_ != NULL) \ + _r_ = glue_io_dispatch.io_ ## _function_(_args_); \ + \ + _r_; \ + } \ + ) + +/* + * this macro-function includes data in 'm_io'. + */ + +#define machine_data_m_io() + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../io.c + */ + +/* + * ../io.c + */ + +t_error glue_io_grant(i_task task, + i_port port, + t_width width); + +t_error glue_io_deny(i_task task, + i_port port, + t_width width); + +t_error glue_io_read(i_task task, + i_port port, + t_width width, + void* data); + +t_error glue_io_write(i_task task, + i_port port, + t_width width, + t_uint64 data); + +t_error glue_io_initialize(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/include/kernel.h b/kaneton/machine/glue/ibm-pc.ia32/educational/include/kernel.h new file mode 100644 index 0000000..60221af --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/include/kernel.h @@ -0,0 +1,65 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...ibm-pc.ia32/educational/include/kernel.h + * + * created julien quintard [sun jun 3 23:54:56 2007] + * updated julien quintard [sun dec 19 13:43:00 2010] + */ + +#ifndef GLUE_KERNEL_H +#define GLUE_KERNEL_H 1 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function defines the kernel dispatcher. + */ + +#define machine_include_kernel() \ + extern d_kernel glue_kernel_dispatch + +/* + * this macro-functio dispatches the kernel calls. + */ + +#define machine_call_kernel(_function_, _args_...) \ + ( \ + { \ + t_error _r_ = ERROR_OK; \ + \ + if (glue_kernel_dispatch.kernel_ ## _function_ != NULL) \ + _r_ = glue_kernel_dispatch.kernel_ ## _function_(_args_); \ + \ + _r_; \ + } \ + ) + +/* + * this macro-function includes data in 'm_kernel'. + */ + +#define machine_data_m_kernel() + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../kernel.c + */ + +/* + * ../kernel.c + */ + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/include/map.h b/kaneton/machine/glue/ibm-pc.ia32/educational/include/map.h new file mode 100644 index 0000000..a49eae3 --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/include/map.h @@ -0,0 +1,71 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...ue/ibm-pc.ia32/educational/include/map.h + * + * created julien quintard [sun jun 3 23:54:56 2007] + * updated julien quintard [sun dec 19 13:43:53 2010] + */ + +#ifndef GLUE_MAP_H +#define GLUE_MAP_H 1 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function defines the map dispatcher. + */ + +#define machine_include_map() \ + extern d_map glue_map_dispatch + +/* + * this macro-function dispatches the map calls. + */ + +#define machine_call_map(_function_, _args_...) \ + ( \ + { \ + t_error _r_ = ERROR_OK; \ + \ + if (glue_map_dispatch.map_ ## _function_ != NULL) \ + _r_ = glue_map_dispatch.map_ ## _function_(_args_); \ + \ + _r_; \ + } \ + ) + +/* + * this macro-function includes data in 'm_map'. + */ + +#define machine_data_m_map() + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../map.c + */ + +/* + * ../map.c + */ + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/include/message.h b/kaneton/machine/glue/ibm-pc.ia32/educational/include/message.h new file mode 100644 index 0000000..72c232b --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/include/message.h @@ -0,0 +1,77 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...bm-pc.ia32/educational/include/message.h + * + * created julien quintard [wed jun 6 16:20:48 2007] + * updated julien quintard [sun dec 19 13:46:24 2010] + */ + +#ifndef GLUE_MESSAGE_H +#define GLUE_MESSAGE_H 1 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function defines the message dispatcher. + */ + +#define machine_include_message() \ + extern d_message glue_message_dispatch + +/* + * this macro-function dispatches the message calls. + */ + +#define machine_call_message(_function_, _args_...) \ + ( \ + { \ + t_error _r_ = ERROR_OK; \ + \ + if (glue_message_dispatch.message_ ## _function_ != NULL) \ + _r_ = glue_message_dispatch.message_ ## _function_(_args_); \ + \ + _r_; \ + } \ + ) + +/* + * this macro-function includes data in 'm_message'. + */ + +#define machine_data_m_message() + +/* + * this macro-function includes data in 'o_message'. + */ + +#define machine_data_o_message() + +/* + * this macro-function includes data in 'o_message_type'. + */ + +#define machine_data_o_message_type() + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../message.c + */ + +/* + * ../message.c + */ + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/include/region.h b/kaneton/machine/glue/ibm-pc.ia32/educational/include/region.h new file mode 100644 index 0000000..972e0d7 --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/include/region.h @@ -0,0 +1,90 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...ibm-pc.ia32/educational/include/region.h + * + * created julien quintard [wed jun 6 16:22:05 2007] + * updated julien quintard [sun dec 19 13:46:56 2010] + */ + +#ifndef GLUE_REGION_H +#define GLUE_REGION_H 1 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function defines the region dispatcher. + */ + +#define machine_include_region() \ + extern d_region glue_region_dispatch + +/* + * this macro-function dispatches the region calls. + */ + +#define machine_call_region(_function_, _args_...) \ + ( \ + { \ + t_error _r_ = ERROR_OK; \ + \ + if (glue_region_dispatch.region_ ## _function_ != NULL) \ + _r_ = glue_region_dispatch.region_ ## _function_(_args_); \ + \ + _r_; \ + } \ + ) + +/* + * this macro-function includes data in 'm_region'. + */ + +#define machine_data_m_region() + +/* + * this macro-function includes data in 'o_region'. + */ + +#define machine_data_o_region() + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../region.c + */ + +/* + * ../region.c + */ + +t_error glue_region_resize(i_as as, + i_region old, + t_vsize size, + i_region* new); + +t_error glue_region_reserve(i_as asid, + i_segment segid, + t_paddr offset, + t_options options, + t_vaddr address, + t_vsize size, + i_region* regid); + +t_error glue_region_release(i_as asid, + i_region regid); + +t_error glue_region_initialize(t_vaddr base, + t_vsize size); + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/include/scheduler.h b/kaneton/machine/glue/ibm-pc.ia32/educational/include/scheduler.h new file mode 100644 index 0000000..dcaa5de --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/include/scheduler.h @@ -0,0 +1,101 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...-pc.ia32/educational/include/scheduler.h + * + * created julien quintard [wed jun 6 16:24:14 2007] + * updated julien quintard [sun dec 19 16:18:38 2010] + */ + +#ifndef GLUE_SCHEDULER_H +#define GLUE_SCHEDULER_H 1 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function defines the scheduler dispatcher. + */ + +#define machine_include_scheduler() \ + extern d_scheduler glue_scheduler_dispatch + +/* + * this macro-function dispatches the scheduler calls. + */ + +#define machine_call_scheduler(_function_, _args_...) \ + ( \ + { \ + t_error _r_ = ERROR_OK; \ + \ + if (glue_scheduler_dispatch.scheduler_ ## _function_ != NULL) \ + _r_ = glue_scheduler_dispatch.scheduler_ ## _function_(_args_); \ + \ + _r_; \ + } \ + ) + +/* + * this macro-function includes data in 'm_scheduler' i.e the scheduler + * manager. + * + * more precisely, the identifier of the timer used to trigger the + * scheduler's context switche routine is kept. this identifier will, + * for example, be used to adjust the delay between the scheduler's + * context switches. + */ + +#define machine_data_m_scheduler() \ + struct \ + { \ + i_timer timer; \ + } machine; + +/* + * this macro-function includes data in 'o_scheduler'. + */ + +#define machine_data_o_scheduler() + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../scheduler.c + */ + +/* + * ../scheduler.c + */ + +t_error glue_scheduler_dump(void); + +t_error glue_scheduler_start(i_cpu cpu); + +t_error glue_scheduler_stop(i_cpu cpu); + +void glue_scheduler_switch(void); + +t_error glue_scheduler_quantum(t_quantum quantum); + +t_error glue_scheduler_yield(void); + +t_error glue_scheduler_initialize(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/include/segment.h b/kaneton/machine/glue/ibm-pc.ia32/educational/include/segment.h new file mode 100644 index 0000000..ae1248b --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/include/segment.h @@ -0,0 +1,95 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...bm-pc.ia32/educational/include/segment.h + * + * created julien quintard [wed jun 6 16:25:05 2007] + * updated julien quintard [sun jan 16 13:41:34 2011] + */ + +#ifndef GLUE_SEGMENT_H +#define GLUE_SEGMENT_H 1 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function defines the segment dispatcher. + */ + +#define machine_include_segment() \ + extern d_segment glue_segment_dispatch + +/* + * this macro-function dispatches the segment calls. + */ + +#define machine_call_segment(_function_, _args_...) \ + ( \ + { \ + t_error _r_ = ERROR_OK; \ + \ + if (glue_segment_dispatch.segment_ ## _function_ != NULL) \ + _r_ = glue_segment_dispatch.segment_ ## _function_(_args_); \ + \ + _r_; \ + } \ + ) + +/* + * this macro-function includes data in 'm_segment'. + */ + +#define machine_data_m_segment() \ + struct \ + { \ + as_gdt gdt; \ + } machine; + +/* + * this macro-function includes data in 'o_segment'. + */ + +#define machine_data_o_segment() + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../segment.c + */ + +/* + * ../segment.c + */ + +t_error glue_segment_dump(void); + +t_error glue_segment_read(i_segment segid, + t_paddr offs, + void* buff, + t_psize sz); + +t_error glue_segment_write(i_segment segid, + t_paddr offs, + const void* buff, + t_psize sz); + +t_error glue_segment_copy(i_segment dst, + t_paddr offsd, + i_segment src, + t_paddr offss, + t_psize sz); + +t_error glue_segment_initialize(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/include/task.h b/kaneton/machine/glue/ibm-pc.ia32/educational/include/task.h new file mode 100644 index 0000000..24da9b0 --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/include/task.h @@ -0,0 +1,96 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...e/ibm-pc.ia32/educational/include/task.h + * + * created julien quintard [wed jun 6 16:25:44 2007] + * updated julien quintard [sun jan 9 22:28:23 2011] + */ + +#ifndef GLUE_TASK_H +#define GLUE_TASK_H 1 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function defines the task dispatcher. + */ + +#define machine_include_task() \ + extern d_task glue_task_dispatch + +/* + * this macro-function dispatches the task calls. + */ + +#define machine_call_task(_function_, _args_...) \ + ( \ + { \ + t_error _r_ = ERROR_OK; \ + \ + if (glue_task_dispatch.task_ ## _function_ != NULL) \ + _r_ = glue_task_dispatch.task_ ## _function_(_args_); \ + \ + _r_; \ + } \ + ) + +/* + * this macro-function includes data in 'm_task'. + */ + +#define machine_data_m_task() + +/* + * this macro-function includes data in the task object i.e 'o_task'. + * + * the embedded data include (i) the I/O map and (ii) a boolean indicating + * if the I/O map must be flushed next time the task is scheduled. + */ + +#define machine_data_o_task() \ + struct \ + { \ + struct \ + { \ + t_uint8 map[ARCHITECTURE_IO_MAP_SIZE]; \ + t_boolean flush; \ + } io; \ + } machine; + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../task.c + */ + +/* + * ../task.c + */ + +t_error glue_task_show(i_task id, + mt_margin margin); + +t_error glue_task_reserve(t_class class, + t_behaviour behav, + t_priority prior, + i_task* id); + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/include/thread.h b/kaneton/machine/glue/ibm-pc.ia32/educational/include/thread.h new file mode 100644 index 0000000..29fdc3f --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/include/thread.h @@ -0,0 +1,172 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...ibm-pc.ia32/educational/include/thread.h + * + * created julien quintard [wed jun 6 16:27:09 2007] + * updated julien quintard [sun jan 30 12:32:44 2011] + */ + +#ifndef GLUE_THREAD_H +#define GLUE_THREAD_H 1 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function defines the thread dispatcher. + */ + +#define machine_include_thread() \ + extern d_thread glue_thread_dispatch + +/* + * this macro-function dispatches the thread calls. + */ + +#define machine_call_thread(_function_, _args_...) \ + ( \ + { \ + t_error _r_ = ERROR_OK; \ + \ + if (glue_thread_dispatch.thread_ ## _function_ != NULL) \ + _r_ = glue_thread_dispatch.thread_ ## _function_(_args_); \ + \ + _r_; \ + } \ + ) + +/* + * this macro-function includes data in the thread manager's structure + * 'm_thread'. + * + * the embedded data include the TSS - Task State Segment structure's + * location along with the several segment selectors CS - Code Segment + * and DS - Data Segment for the various task classes: kernel, driver, + * service and guest. + */ + +#define machine_data_m_thread() \ + struct \ + { \ + t_vaddr tss; \ + \ + struct \ + { \ + struct \ + { \ + t_uint16 cs; \ + t_uint16 ds; \ + } kernel; \ + \ + struct \ + { \ + t_uint16 cs; \ + t_uint16 ds; \ + } driver; \ + \ + struct \ + { \ + t_uint16 cs; \ + t_uint16 ds; \ + } service; \ + \ + struct \ + { \ + t_uint16 cs; \ + t_uint16 ds; \ + } guest; \ + } selectors; \ + } machine; + +/* + * this macro-function includes data in the thread object's structure + * 'o_thread'. + * + * these include the thread's kernel stack referred to as the _pile_. + * indeed, whenever an interrupt or exception is triggered, the + * thread's context is stored on the thread's pile i.e not its stack. + * + * however, a special case exists for ring0 threads i.e kernel threads. + * when these threads are interrupted, their context is stored on the + * current stack since no change in privilege occurs. + * + * the 'context' attribute contains the location, within the thread's + * address space, of the thread's IA32 context. this context, depending on + * the thread's privilege, can be located either on the stack or the pile. + * + * note that the 'stack.pointer' and 'pile.pointer' are static values---i.e + * do not change over time---referencing the top of the stack/pile. + */ + +#define machine_data_o_thread() \ + struct \ + { \ + t_vaddr context; \ + \ + struct \ + { \ + t_vaddr pointer; \ + } stack; \ + \ + struct \ + { \ + t_vaddr base; \ + t_vsize size; \ + t_vaddr pointer; \ + } pile; \ + } machine; + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../thread.c + */ + +/* + * ../thread.c + */ + +t_error glue_thread_show(i_thread id, + mt_margin margin); + +t_error glue_thread_dump(void); + +t_error glue_thread_reserve(i_task task, + t_priority priority, + t_vaddr stack, + t_vsize stacksz, + t_vaddr entry, + i_thread* id); + +t_error glue_thread_release(i_thread id); + +t_error glue_thread_load(i_thread id, + s_thread_context context); + +t_error glue_thread_store(i_thread id, + s_thread_context* context); + +t_error glue_thread_arguments(i_thread id, + void* arguments, + t_vsize size); + +t_error glue_thread_initialize(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/include/timer.h b/kaneton/machine/glue/ibm-pc.ia32/educational/include/timer.h new file mode 100644 index 0000000..c37028a --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/include/timer.h @@ -0,0 +1,76 @@ + +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane.../ibm-pc.ia32/educational/include/timer.h + * + * created julien quintard [wed jun 6 16:29:41 2007] + * updated julien quintard [sun dec 19 16:23:00 2010] + */ + +#ifndef GLUE_TIMER_H +#define GLUE_TIMER_H 1 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function defines the timer dispatcher. + */ + +#define machine_include_timer() \ + extern d_timer glue_timer_dispatch + +/* + * this macro-function dispatches the timer calls. + */ + +#define machine_call_timer(_function_, _args_...) \ + ( \ + { \ + t_error _r_ = ERROR_OK; \ + \ + if (glue_timer_dispatch.timer_ ## _function_ != NULL) \ + _r_ = glue_timer_dispatch.timer_ ## _function_(_args_); \ + \ + _r_; \ + } \ + ) + +/* + * this macro-function includes data in 'm_timer'. + */ + +#define machine_data_m_timer() + +/* + * this macro-function includes data in 'o_timer'. + */ + +#define machine_data_o_timer() + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../timer.c + */ + +/* + * ../timer.c + */ + +t_error glue_timer_initialize(void); + +t_error glue_timer_clean(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/io.c b/kaneton/machine/glue/ibm-pc.ia32/educational/io.c new file mode 100644 index 0000000..524ae0b --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/io.c @@ -0,0 +1,173 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...achine/glue/ibm-pc.ia32/educational/io.c + * + * created matthieu bucchianeri [sat jul 29 18:04:35 2006] + * updated julien quintard [sun jan 9 22:50:43 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file implements the I/O manager's glue. + * + * note that the read and write functions *should not* be used in servers. + * indeed, the direct use of assembly instructions such as INB, INW, INL, + * OUTB, OUTW, and OUTL should be preferred because (i) these functions lead + * to very bad performance and (ii) some servers, the drivers, benefit from + * special hardware privileges granting them access to any port. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the I/O dispatcher. + */ + +d_io glue_io_dispatch = + { + glue_io_grant, + glue_io_deny, + glue_io_read, + glue_io_write, + glue_io_initialize, + NULL + }; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function enables I/Os on the given port for the given task + * by properly setting the I/O bitmap. + */ + +t_error glue_io_grant(i_task task, + i_port port, + t_width width) +{ + if (architecture_io_grant(task, port, width) != ERROR_OK) + MACHINE_ESCAPE("unable to set the I/O bitmap"); + + MACHINE_LEAVE(); +} + +/* + * this function denies access on the given I/O port. + */ + +t_error glue_io_deny(i_task task, + i_port port, + t_width width) +{ + if (architecture_io_deny(task, port, width) != ERROR_OK) + MACHINE_ESCAPE("unable to set the I/O bitmap"); + + MACHINE_LEAVE(); +} + +/* + * this function reads data from one or more I/O registers. + */ + +t_error glue_io_read(i_task task, + i_port port, + t_width width, + void* data) +{ + switch (width) + { + case IO_WIDTH_8: + { + ARCHITECTURE_IO_IN_8(port, *((t_uint8*)data)); + + break; + } + case IO_WIDTH_16: + { + ARCHITECTURE_IO_IN_16(port, *((t_uint16*)data)); + + break; + } + case IO_WIDTH_32: + { + ARCHITECTURE_IO_IN_32(port, *((t_uint32*)data)); + + break; + } + case IO_WIDTH_64: + MACHINE_ESCAPE("unsupported width '%u'", + width); + default: + MACHINE_ESCAPE("unknown width '%u'", + width); + } + + MACHINE_LEAVE(); +} + +/* + * this function writes data to one or more I/O ports. + */ + +t_error glue_io_write(i_task task, + i_port port, + t_width width, + t_uint64 data) +{ + switch (width) + { + case IO_WIDTH_8: + { + ARCHITECTURE_IO_OUT_8(port, (t_uint8)data); + + break; + } + case IO_WIDTH_16: + { + ARCHITECTURE_IO_OUT_16(port, (t_uint16)data); + + break; + } + case IO_WIDTH_32: + { + ARCHITECTURE_IO_OUT_32(port, (t_uint32)data); + + break; + } + case IO_WIDTH_64: + MACHINE_ESCAPE("unsupported width '%u'", + width); + default: + MACHINE_ESCAPE("unknown width '%u'", + width); + } + + MACHINE_LEAVE(); +} + +/* + * this function initialize the I/O manager. + */ + +t_error glue_io_initialize(void) +{ + if (architecture_io_reset() != ERROR_OK) + MACHINE_ESCAPE("unable to reset the IOPL - I/O Privilege Level"); + + MACHINE_LEAVE(); +} diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/kernel.c b/kaneton/machine/glue/ibm-pc.ia32/educational/kernel.c new file mode 100644 index 0000000..15ebac8 --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/kernel.c @@ -0,0 +1,39 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...ne/glue/ibm-pc.ia32/educational/kernel.c + * + * created matthieu bucchianeri [sat jun 16 18:10:38 2007] + * updated julien quintard [sat jan 15 06:10:17 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file implements the kernel manager's glue. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the kernel dispatcher. + */ + +d_kernel glue_kernel_dispatch = + { + NULL, + NULL, + NULL + }; diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/layout/driver.lyt b/kaneton/machine/glue/ibm-pc.ia32/educational/layout/driver.lyt new file mode 100644 index 0000000..0012a9c --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/layout/driver.lyt @@ -0,0 +1,30 @@ +OUTPUT_FORMAT("elf32-i386") +OUTPUT_ARCH("i386") +ENTRY(_start) + +PHDRS +{ + global PT_LOAD; +} + +SECTIONS +{ + . = 0x80000000 + SIZEOF_HEADERS; + + .text ALIGN(0x1000): + { + *(.text*) + } : global + + .rodata ALIGN(0x1000): + { + *(.rodata*) + } : global + + .data ALIGN(0x1000): + { + *(.data*) *(.bss) *(COMMON) + } : global + + _end = .; +} diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/layout/guest.lyt b/kaneton/machine/glue/ibm-pc.ia32/educational/layout/guest.lyt new file mode 100644 index 0000000..7193f41 --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/layout/guest.lyt @@ -0,0 +1,30 @@ +OUTPUT_FORMAT("elf32-i386") +OUTPUT_ARCH("i386") +ENTRY(_start) + +PHDRS +{ + code PT_LOAD; + rodata PT_LOAD; + data PT_LOAD; +} + +SECTIONS +{ + . = 0x40000000 + SIZEOF_HEADERS; + + .text ALIGN(0x1000): + { + *(.text*) + } : code + + .rodata ALIGN(0x1000): + { + *(.rodata*) + } : rodata + + .data ALIGN(0x1000): + { + *(.data*) *(.bss) *(COMMON) + } : data +} diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/layout/kernel.lyt b/kaneton/machine/glue/ibm-pc.ia32/educational/layout/kernel.lyt new file mode 100644 index 0000000..c8aa71a --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/layout/kernel.lyt @@ -0,0 +1,37 @@ +OUTPUT_FORMAT("elf32-i386") +OUTPUT_ARCH("i386") +ENTRY(kaneton) + +PHDRS +{ + uniq PT_LOAD; +} + +SECTIONS +{ + . = 0x01000000 + SIZEOF_HEADERS; + + .text : + { + *(.text*) + . = ALIGN(0x1000); + _handler_code_begin = .; + *(.handler_code) + . = ALIGN(0x1000); + _handler_code_end = .; + } : uniq + + .rodata : + { + *(.rodata*) + } : uniq + + .data : + { + *(.data*) *(.bss) *(COMMON) + _handler_data_begin = .; + *(.handler_data) + . = ALIGN(0x1000); + _handler_data_end = .; + } : uniq +} diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/layout/service.lyt b/kaneton/machine/glue/ibm-pc.ia32/educational/layout/service.lyt new file mode 100644 index 0000000..7e4e550 --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/layout/service.lyt @@ -0,0 +1,30 @@ +OUTPUT_FORMAT("elf32-i386") +OUTPUT_ARCH("i386") +ENTRY(_start) + +PHDRS +{ + code PT_LOAD; + rodata PT_LOAD; + data PT_LOAD; +} + +SECTIONS +{ + . = 0x60000000 + SIZEOF_HEADERS; + + .text ALIGN(0x1000): + { + *(.text*) + } : code + + .rodata ALIGN(0x1000): + { + *(.rodata*) + } : rodata + + .data ALIGN(0x1000): + { + *(.data*) *(.bss) *(COMMON) + } : data +} diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/map.c b/kaneton/machine/glue/ibm-pc.ia32/educational/map.c new file mode 100644 index 0000000..fe1420c --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/map.c @@ -0,0 +1,41 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...chine/glue/ibm-pc.ia32/educational/map.c + * + * created matthieu bucchianeri [sat jun 16 18:10:38 2007] + * updated julien quintard [sat jan 15 06:10:22 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file implements the map manager's glue. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the map dispatcher. + */ + +d_map glue_map_dispatch = + { + NULL, + NULL, + NULL, + NULL, + NULL + }; diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/message.c b/kaneton/machine/glue/ibm-pc.ia32/educational/message.c new file mode 100644 index 0000000..0c453ad --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/message.c @@ -0,0 +1,50 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license + * + * file /home/mycure/kane...e/glue/ibm-pc.ia32/educational/message.c + * + * created matthieu bucchianeri [sun jun 17 18:16:18 2007] + * updated julien quintard [sat jan 15 06:45:07 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file implements the message manager's glue. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the message dispatcher. + */ + +d_message glue_message_dispatch = + { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL + }; diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/region.c b/kaneton/machine/glue/ibm-pc.ia32/educational/region.c new file mode 100644 index 0000000..e7c167a --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/region.c @@ -0,0 +1,49 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...ne/glue/ibm-pc.ia32/educational/region.c + * + * created julien quintard [wed dec 14 07:06:44 2005] + * updated julien quintard [sat feb 5 14:37:43 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file implements the region manager's glue which basically consists + * in updating the address space's page directory, tables etc. in order + * to reflect the core operation. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the region dispatcher. + */ + +d_region glue_region_dispatch = + { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL + }; diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/scheduler.c b/kaneton/machine/glue/ibm-pc.ia32/educational/scheduler.c new file mode 100644 index 0000000..3296be6 --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/scheduler.c @@ -0,0 +1,219 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...glue/ibm-pc.ia32/educational/scheduler.c + * + * created matthieu bucchianeri [sat jun 3 22:45:19 2006] + * updated julien quintard [tue feb 15 21:03:52 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file implements the scheduler manager's glue. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the scheduler manager. + */ + +extern m_scheduler _scheduler; + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the scheduler dispatcher. + */ + +d_scheduler glue_scheduler_dispatch = + { + NULL, + glue_scheduler_dump, + glue_scheduler_start, + glue_scheduler_stop, + glue_scheduler_quantum, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL + }; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function dumps the scheduler manager's machine part. + */ + +t_error glue_scheduler_dump(void) +{ + module_call(console, message, + '#', + " machine: timer(%qd)\n", + _scheduler.machine.timer); + + MACHINE_LEAVE(); +} + +/* + * this function starts the scheduler. + * + * steps: + * + * 1) reserve a timer so that a context switch is triggered every quantum + * milliseconds. + */ + +t_error glue_scheduler_start(i_cpu cpu) +{ + /* + * 1) + */ + + if (timer_reserve(TIMER_TYPE_FUNCTION, + TIMER_ROUTINE(glue_scheduler_switch), + TIMER_DATA(NULL), + _scheduler.quantum, + TIMER_OPTION_REPEAT, + &_scheduler.machine.timer) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve the timer"); + + MACHINE_LEAVE(); +} + +/* + * this function contributes to stopping the scheduler by manually triggering + * the timer interrupt in order to induce an immediate scheduler election. + * + * during this election, the scheduler will notice the scheduler's state + * has changed to stop and will do the necessary to stop electing threads. + * + * 1) retrieve the current CPU's scheduler. + * 2) if the CPU to stop is the current one, yield the execution in order + * to induce a scheduler election. + * 3) release the scheduler timer. + */ + +t_error glue_scheduler_stop(i_cpu cpu) +{ + o_scheduler* scheduler; + + /* + * 1) + */ + + if (scheduler_current(&scheduler) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the current scheduler"); + + /* + * 2) + */ + + if (scheduler->cpu == cpu) + { + if (scheduler_yield() != ERROR_OK) + MACHINE_ESCAPE("unable to yield the execution"); + } + + /* + * 3) + */ + + if (timer_release(_scheduler.machine.timer) != ERROR_OK) + MACHINE_ESCAPE("unable to release the timer"); + + MACHINE_LEAVE(); +} + +/* + * this handler is triggered whenever the scheduler quantum of time has + * elapsed. this function requests the scheduler to elect a new thread + * and performs the context switch. + * + * steps: + * + * 1) retrieve the current CPU's scheduler. + * 2) save the identifier of the currently running thread. + * 3) request a thread election to the scheduler. + * 4) save the identifier of the thread about to be executed. + * 5) perform a context switch between the two threads. + */ + +void glue_scheduler_switch(void) +{ + o_scheduler* scheduler; + i_thread current; + i_thread future; + + /* + * 1) + */ + + assert(scheduler_current(&scheduler) == ERROR_OK); + + /* + * 2) + */ + + current = scheduler->thread; + + /* + * 3) + */ + + assert(scheduler_elect() == ERROR_OK); + + /* + * 4) + */ + + future = scheduler->thread; + + /* + * 5) + */ + + assert(architecture_context_switch(current, future) == ERROR_OK); +} + +/* + * this function sets the scheduler quantum value. + * + * steps: + * + * 1) note that since the quantum has been updated, the scheduler timer + * must also be adujsted. + */ + +t_error glue_scheduler_quantum(t_quantum quantum) +{ + /* + * 1) + */ + + if (timer_update(_scheduler.machine.timer, quantum) != ERROR_OK) + MACHINE_ESCAPE("unable to adjust the timer's delay to the " + "scheduler's quantum"); + + MACHINE_LEAVE(); +} diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/segment.c b/kaneton/machine/glue/ibm-pc.ia32/educational/segment.c new file mode 100644 index 0000000..524093d --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/segment.c @@ -0,0 +1,189 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...e/glue/ibm-pc.ia32/educational/segment.c + * + * created julien quintard [fri feb 11 03:04:40 2005] + * updated julien quintard [fri apr 8 09:04:59 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file implements the segment manager's glue. + * + * the machine behaviour consists in managing the IA32 segmentation model + * through the flat protected mode. + * + * [XXX:improvement] the segment_permissions() should be handled from the + * architecture point of view in order to update the + * mappings with the new permissions. the problem regarding + * how to identify the regions mapping a segment applies + * once again, as for segment_resize(). + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the segment manager. + */ + +extern m_segment _segment; + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the segment dispatcher. + */ + +d_segment glue_segment_dispatch = + { + NULL, + glue_segment_dump, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + glue_segment_read, + glue_segment_write, + glue_segment_copy, + NULL, + NULL, + NULL, + NULL, + glue_segment_initialize, + NULL + }; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function displays additional machine-specific information on + * the segment manager. + * + * steps: + * + * 1) display the machine-specific attributes. + */ + +t_error glue_segment_dump(void) +{ + /* + * 1) + */ + + module_call(console, message, + '#', + " machine:\n"); + + module_call(console, message, + '#', + " gdt: table(0x%x) size(0x%x):\n", + _segment.machine.gdt.table, + _segment.machine.gdt.size); + + MACHINE_LEAVE(); +} + +/* + * this function reads directly from a segment to a buffer. + * + * this function is useful a a virtual environment since the necessary + * pages will be temporary mapped for the operation to be carried out. + */ + +t_error glue_segment_read(i_segment segid, + t_paddr offs, + void* buff, + t_psize sz) +{ + if (architecture_paging_read(segid, offs, buff, sz) != ERROR_OK) + MACHINE_ESCAPE("unable to read data from the segment"); + + MACHINE_LEAVE(); +} + +/* + * this function writes directly to a segment from a buffer. + * + * this function is useful a a virtual environment since the necessary + * pages will be temporary mapped for the operation to be carried out. + */ + +t_error glue_segment_write(i_segment segid, + t_paddr offs, + const void* buff, + t_psize sz) +{ + if (architecture_paging_write(segid, offs, buff, sz) != ERROR_OK) + MACHINE_ESCAPE("unable to write data to the segment"); + + MACHINE_LEAVE(); +} + +/* + * this function copies a chunk of data from one segment to another. + * + * this function is useful a a virtual environment since the necessary + * pages will be temporary mapped for the operation to be carried out. + */ + +t_error glue_segment_copy(i_segment dst, + t_paddr offsd, + i_segment src, + t_paddr offss, + t_psize sz) +{ + if (architecture_paging_copy(dst, offsd, src, offss, sz) != ERROR_OK) + MACHINE_ESCAPE("unable to copy data from a segment to another"); + + MACHINE_LEAVE(); +} + +/* + * this function initializes the segment manager's glue. + * + * steps: + * + * 1) set up the protected mode. + * 2) set up the segmentation unit. + */ + +t_error glue_segment_initialize(void) +{ + /* + * 1) + */ + + if (architecture_pmode_setup() != ERROR_OK) + MACHINE_ESCAPE("unable to set up the protected mode"); + + /* + * 2) + */ + + if (architecture_segmentation_setup() != ERROR_OK) + MACHINE_ESCAPE("unable to initialize the IA32 segmentation"); + + MACHINE_LEAVE(); +} diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/task.c b/kaneton/machine/glue/ibm-pc.ia32/educational/task.c new file mode 100644 index 0000000..c4b3266 --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/task.c @@ -0,0 +1,131 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...hine/glue/ibm-pc.ia32/educational/task.c + * + * created matthieu bucchianeri [sat jun 16 18:10:38 2007] + * updated julien quintard [fri jan 28 14:07:21 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file implements the task manager's glue. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the task dispatcher. + */ + +d_task glue_task_dispatch = + { + glue_task_show, + NULL, + glue_task_reserve, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + }; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function shows the given task's machine-related data. + * + * steps: + * + * 1) retrieve the task object. + * 2) display the machine-specific information. + */ + +t_error glue_task_show(i_task id, + mt_margin margin) +{ + o_task* o; + + /* + * 1) + */ + + if (task_get(id, &o) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " machine:\n", + MODULE_CONSOLE_MARGIN_VALUE(margin)); + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " I/O: map(0x%x) flush(%s)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + &o->machine.io.map, + o->machine.io.flush == BOOLEAN_TRUE ? "true" : "false"); + + MACHINE_LEAVE(); +} + +/* + * this function initializes the I/O map in order to terminate the + * task reservation process. + * + * steps: + * + * 1) retrieve the task object. + * 2) clear the I/O map. + */ + +t_error glue_task_reserve(t_class class, + t_behaviour behav, + t_priority prior, + i_task* id) +{ + o_task* o; + + /* + * 1) + */ + + if (task_get(*id, &o) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the task object"); + + /* + * 2) + */ + + if (architecture_io_clear(*id) != ERROR_OK) + MACHINE_ESCAPE("unable to clear the I/O bitmap"); + + o->machine.io.flush = BOOLEAN_FALSE; + + MACHINE_LEAVE(); +} diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/thread.c b/kaneton/machine/glue/ibm-pc.ia32/educational/thread.c new file mode 100644 index 0000000..3856271 --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/thread.c @@ -0,0 +1,352 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...ne/glue/ibm-pc.ia32/educational/thread.c + * + * created renaud voltz [tue apr 4 03:08:03 2006] + * updated julien quintard [sun jan 30 21:01:55 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file implements the thread manager's glue. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- externs --------------------------------------------------------- + */ + +/* + * the thread manager. + */ + +extern m_thread _thread; + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the thread dispatcher. + */ + +d_thread glue_thread_dispatch = + { + glue_thread_show, + NULL, + glue_thread_reserve, + glue_thread_release, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + glue_thread_arguments, + NULL, + NULL, + glue_thread_load, + glue_thread_store, + glue_thread_initialize, + NULL + }; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function shows a thread's machine-specific data. + * + * steps: + * + * 1) retrieve the thread object. + * 2) retrieve the thread's IA32 context. + * 3) display the thread's machine-specific information. + */ + +t_error glue_thread_show(i_thread id, + mt_margin margin) +{ + o_thread* thread; + as_context ctx; + + /* + * 1) + */ + + if (thread_get(id, &thread) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the thread object"); + + /* + * 2) + */ + + if (architecture_context_get(id, &ctx) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the thread's IA32 context"); + + /* + * 3) + */ + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " machine: context(0x%x)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + thread->machine.context); + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " stack: pointer(0x%x)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + thread->machine.stack.pointer); + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " pile: base(0x%x) size(0x%x) pointer(0x%x)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + thread->machine.pile.base, + thread->machine.pile.size, + thread->machine.pile.pointer); + + module_call(console, message, + '#', + MODULE_CONSOLE_MARGIN_FORMAT + " context: eax(0x%x) ebx(0x%x) ecx(0x%x) edx(0x%x) " + "esi(0x%x) edi(0x%x) ebp(0x%x) _esp(0x%x) esp(0x%x) eip(0x%x) " + "eflags(0x%x) cs(0x%x) ds(0x%x) ss(0x%x)\n", + MODULE_CONSOLE_MARGIN_VALUE(margin), + ctx.eax, + ctx.ebx, + ctx.ecx, + ctx.edx, + ctx.esi, + ctx.edi, + ctx.ebp, + ctx._esp, + ctx.esp, + ctx.eip, + ctx.eflags, + ctx.cs & 0xffff, + ctx.ds & 0xffff, + ctx.ss & 0xffff); + + MACHINE_LEAVE(); +} + +/* + * this function dumps the thread manager's machine-specific information. + * + * steps: + * + * 1) display a general message. + * 2) display the segment selectors. + */ + +t_error glue_thread_dump(void) +{ + /* + * 1) + */ + + module_call(console, message, + '#', + " machine:\n"); + + /* + * 2) + */ + + module_call(console, message, + '#', + " selectors:\n"); + + module_call(console, message, + '#', + " kernel: CS(0x%x) DS(0x%x)\n", + _thread.machine.selectors.kernel.cs, + _thread.machine.selectors.kernel.ds); + + module_call(console, message, + '#', + " driver: CS(0x%x) DS(0x%x)\n", + _thread.machine.selectors.driver.cs, + _thread.machine.selectors.driver.ds); + + module_call(console, message, + '#', + " service: CS(0x%x) DS(0x%x)\n", + _thread.machine.selectors.service.cs, + _thread.machine.selectors.service.ds); + + module_call(console, message, + '#', + " guest: CS(0x%x) DS(0x%x)\n", + _thread.machine.selectors.guest.cs, + _thread.machine.selectors.guest.ds); + + MACHINE_LEAVE(); +} + +/* + * this function reserves a thread by initializing its hardware context. + * + * steps: + * + * 1) initialize the thread's IA32 context. + */ + +t_error glue_thread_reserve(i_task task, + t_priority priority, + t_vaddr stack, + t_vsize stacksz, + t_vaddr entry, + i_thread* id) +{ + /* + * 1) + */ + + if (architecture_context_build(*id) != ERROR_OK) + MACHINE_ESCAPE("unable to build the IA32 context"); + + MACHINE_LEAVE(); +} + +/* + * this function releases the thread's IA32-specific information. + * + * steps: + * + * 1) destory the IA32 context. + */ + +t_error glue_thread_release(i_thread id) +{ + /* + * 1) + */ + + if (architecture_context_destroy(id) != ERROR_OK) + MACHINE_ESCAPE("unable to destory the IA32 context"); + + MACHINE_LEAVE(); +} + +/* + * this function loads the given context in the thread low-level structure. + * + * steps: + * + * 1) retrieve the thread's IA32 context. + * 2) set some of the context's attributes: PC and SP. + * 3) upadte the thread's IA32 context. + */ + +t_error glue_thread_load(i_thread id, + s_thread_context context) +{ + as_context ctx; + + /* + * 1) + */ + + if (architecture_context_get(id, &ctx) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the thread's IA32 context"); + + /* + * 2) + */ + + ctx.eip = context.pc; + ctx.esp = context.sp; + + /* + * 3) + */ + + if (architecture_context_set(id, &ctx) != ERROR_OK) + MACHINE_ESCAPE("unable to update the IA32 context"); + + MACHINE_LEAVE(); +} + +/* + * this function returns the thread's current context. + * + * steps: + * + * 1) retrieve the thread's IA32 context. + * 2) return the only core-meaningful values: PC and SP. + */ + +t_error glue_thread_store(i_thread id, + s_thread_context* context) +{ + as_context ctx; + + /* + * 1) + */ + + if (architecture_context_get(id, &ctx) != ERROR_OK) + MACHINE_ESCAPE("unable to retrieve the IA32 context"); + + /* + * 2) + */ + + context->pc = ctx.eip; + context->sp = ctx.esp; + + MACHINE_LEAVE(); +} + +/* + * this function sets the given arguments on the thread's stack. + */ + +t_error glue_thread_arguments(i_thread id, + void* arguments, + t_vsize size) +{ + if (architecture_context_arguments(id, arguments, size) != ERROR_OK) + MACHINE_ESCAPE("unable to push the arguments"); + + MACHINE_LEAVE(); +} + +/* + * this function initializes the thread manager's glue. + * + * steps: + * + * 1) initialize the context switcher. + */ + +t_error glue_thread_initialize(void) +{ + /* + * 1) + */ + + if (architecture_context_setup() != ERROR_OK) + MACHINE_ESCAPE("unable to set up the context switcher"); + + MACHINE_LEAVE(); +} diff --git a/kaneton/machine/glue/ibm-pc.ia32/educational/timer.c b/kaneton/machine/glue/ibm-pc.ia32/educational/timer.c new file mode 100644 index 0000000..411842b --- /dev/null +++ b/kaneton/machine/glue/ibm-pc.ia32/educational/timer.c @@ -0,0 +1,112 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...ine/glue/ibm-pc.ia32/educational/timer.c + * + * created julien quintard [mon jun 11 05:41:14 2007] + * updated julien quintard [sun jan 30 20:42:39 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file implements the timer manager's glue. + * + * the machine relies on the platform which provides a PIT - Programmable + * Interval Timer. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the timer dispatcher. + */ + +d_timer glue_timer_dispatch = + { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + glue_timer_initialize, + glue_timer_clean, + }; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function initializes the timer manager's glue. + * + * steps: + * + * 1) initialize the PIT - Programmable Interrupt Timer. + * 2) reserve the timer event in order to trigger a function on a regular + * basis. + */ + +t_error glue_timer_initialize(void) +{ + /* + * 1) + */ + + if (platform_pit_initialize() != ERROR_OK) + MACHINE_ESCAPE("unable to initialize the PIT"); + + /* + * 2) + */ + + if (event_reserve(ARCHITECTURE_IDT_IRQ_PIT, + EVENT_TYPE_FUNCTION, + EVENT_ROUTINE(timer_handler), + 0) != ERROR_OK) + MACHINE_ESCAPE("unable to reserve the event corresponding to the hardware " + "timer interrupt"); + + MACHINE_LEAVE(); +} + +/* + * this function cleans the timer manager's glue. + * + * steps: + * + * 1) release the timer event. + * 2) clean the PIT. + */ + +t_error glue_timer_clean(void) +{ + /* + * 1) + */ + + if (event_release(ARCHITECTURE_IDT_IRQ_PIT) != ERROR_OK) + MACHINE_ESCAPE("unable to release the timer IRQ"); + + /* + * 2) + */ + + if (platform_pit_clean() != ERROR_OK) + MACHINE_ESCAPE("unable to clean the PIT"); + + MACHINE_LEAVE(); +} diff --git a/kaneton/machine/include/machine.h b/kaneton/machine/include/machine.h new file mode 100644 index 0000000..5fd84d8 --- /dev/null +++ b/kaneton/machine/include/machine.h @@ -0,0 +1,157 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/machine/include/machine.h + * + * created julien quintard [thu jun 7 12:46:25 2007] + * updated julien quintard [sun dec 19 12:04:58 2010] + */ + +#ifndef MACHINE_H +#define MACHINE_H 1 + +/* + * ---------- information ----------------------------------------------------- + * + * note that the machine's components, especially the architecture, are + * supposed to provide the following meta definitions. + * + * ___kaneton$endian, the architecture's endian being ENDIAN_LITTLE, + * ENDIAN_BIG etc. + * ___kaneton$wordsz, the architecture's word size in bytes. + * ___kaneton$framesz, the physical memory unit i.e frame size in bytes. + * ___kaneton$pagesz, the virtual memory unit i.e page size in bytes. + * + * in addition, the kaneton environment should provide the following + * definitions when building the microkernel and its servers: + * + * ___kaneton, indicates that kaneton is the current kernel. + * ___kaneton$kernel, indicates that the currently running entity is + * the kernel. + * ___kaneton$server, indicates that the currently running entity is + * a server, being a driver, service or a guest. + * ___kaneton$driver, indicates that the currently running entity is + * a driver i.e a server which has access to the hardware. + * ___kaneton$service, indicates that the currently running entity is + * a service i.e a server offering some functionalities + * other servers can request. + * ___kaneton$guest, indicates the the currently running entity is a + * guest i.e a non-privileged server. + * + * some of these meta definitions are actually used by the core. for instance + * the memory managers rely on both ___kaneton$framesz and ___kaneton$pagesz. + * + * + * + * in addition, the machine must declare a certain number of types which + * are listed below: + * + * t_uint8, an unsigned 8-bit type. + * t_sint8, a signed 8-bit type. + * t_uint16, an unsigned 16-bit type. + * t_sint16, a signed 16-bit type + * t_uint32, an unsigned 32-bit type. + * t_sint32, a signed 32-bit type. + * t_uint64, an unsigned 64-bit type + * t_sint64, a signed 64-bit type + * t_reg8, an 8-bit register. + * t_reg16, a 16-bit register. + * t_reg32, a 32-bit register. + * t_reg64, a 64-bit register. + * + * but also: + * + * t_paddr, an address in the physical memory space. + * t_psize, the size of an area of physical memory. + * t_vaddr, an address in the virtual memory space. + * t_vsize, the size of an area of virtual memory. + * + * [XXX:improvement] for now, the ___kaneton${kernel,driver,service,guest} + * meta definitions are not provided! they should be + * through the compilation process which knows what + * it is building, which layout---i.e LD script---it is + * using. + */ + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * the machine_include() is used to include in the core definitions provided + * by the machine such as the dispatchers' definitions for instance. + */ + +#define machine_include(_manager_) \ + machine_include_ ## _manager_() + +/* + * the machine_call() macro-function enables a function to call its + * machine counterpart, should the platform, architecture or both need to + * perform hardware operations. note that kaneton has been designed around + * this portable scheme. however a few rules apply, especially regarding + * the placement of these machine_call()s. indeed, should the object on which + * the operation applies be a creation or modification, the machine_call() + * must be placed at the end of the core's function, once everything has + * been performed by the core, with a few exceptions i.e if one of the core's + * actions may prevent the machine from performing its part. for an example, + * please refer to thread_sleep() for example. should the object be removed + * however, the machine should be called prior to performing anything in + * the core as the machine may need to undo was has been done at the + * object's creation. for more information, the reader is advised to browser + * through the core's managers in order to see when the machine is called. + */ + +#define machine_call(_manager_, _function_, _arguments_...) \ + machine_call_ ## _manager_(_function_, ##_arguments_) + +/* + * finally, machine_data() is used for including machine-specific information + * within core managers, objects, structures etc. indeed, for example, on + * the IA32 architecture, every address space is associated with a so-called + * page directory. the address of this page directory must be kept somewhere + * so that the machine can perform its machine-specific adjustments whenever + * a memory management operation is requested. although the machine could + * maintain data structures linking an address space identifier to its + * page directory, it is far easier to include the page directory's address + * directly in the address space object. + */ + +#define machine_data(_object_) \ + machine_data_ ## _object_() + +/* + * escape + */ + +#define MACHINE_ESCAPE(_format_, _arguments_...) \ + { \ + module_call(report, record, \ + _format_ " (%s:%u)", \ + ##_arguments_, __FUNCTION__, __LINE__); \ + \ + return (ERROR_KO); \ + } + +/* + * leave + */ + +#define MACHINE_LEAVE() \ + { \ + return (ERROR_OK); \ + } + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include +#include +#include + +#endif diff --git a/kaneton/machine/include/types.h b/kaneton/machine/include/types.h new file mode 100644 index 0000000..2dbc0b4 --- /dev/null +++ b/kaneton/machine/include/types.h @@ -0,0 +1,19 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/machine/include/types.h + * + * created julien quintard [thu jun 7 14:01:50 2007] + * updated julien quintard [thu jun 7 14:17:33 2007] + */ + +#ifndef MACHINE_TYPES_H +#define MACHINE_TYPES_H 1 + +#include + +#endif diff --git a/kaneton/machine/platform/ibm-pc/Makefile b/kaneton/machine/platform/ibm-pc/Makefile new file mode 100644 index 0000000..d85bb83 --- /dev/null +++ b/kaneton/machine/platform/ibm-pc/Makefile @@ -0,0 +1,82 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kane...kaneton/machine/platform/ibm-pc/Makefile +# +# created julien quintard [sun dec 10 18:38:17 2006] +# updated julien quintard [thu dec 9 14:37:23 2010] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/machine/platform + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers + +# +# ---------- variables -------------------------------------------------------- +# + +PLATFORM_C := console.c \ + pic.c \ + pit.c \ + rtc.c \ + serial.c + +PLATFORM_O := $(PLATFORM_C:.c=.o) + +PLATFORM_INCLUDE := $(_PLATFORM_INCLUDE_DIR_)/console.h \ + $(_PLATFORM_INCLUDE_DIR_)/pic.h \ + $(_PLATFORM_INCLUDE_DIR_)/pit.h \ + $(_PLATFORM_INCLUDE_DIR_)/rtc.h \ + $(_PLATFORM_INCLUDE_DIR_)/serial.h + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: $(_PLATFORM_LO_) + +$(_PLATFORM_LO_): $(PLATFORM_O) + $(call env_remove,$(_PLATFORM_LO_),) + + $(call env_archive,$(_PLATFORM_LO_),$(PLATFORM_O),) + +clear: + $(call env_remove,$(PLATFORM_O),) + + $(call env_remove,$(_PLATFORM_LO_),) + + $(call env_purge,) + +prototypes: + $(call env_prototypes,$(PLATFORM_INCLUDE),) + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(PLATFORM_C),) + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/machine/platform/ibm-pc/console.c b/kaneton/machine/platform/ibm-pc/console.c new file mode 100644 index 0000000..2977e27 --- /dev/null +++ b/kaneton/machine/platform/ibm-pc/console.c @@ -0,0 +1,258 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...aneton/machine/platform/ibm-pc/console.c + * + * created julien quintard [sat may 28 18:23:13 2005] + * updated julien quintard [thu apr 7 15:13:32 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file implements basic console functionalities. these functionalities + * are mainly use during the kernel boot though a dedicated console service + * will be launched by the 'system' initial service. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +#include + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the console manager. + */ + +pm_console _platform_console; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function just clears the console. + */ + +void platform_console_clear(void) +{ + t_uint16 i; + + for (i = 0; i < PLATFORM_CONSOLE_SIZE; i++) + { + _platform_console.vga[i + 0] = 0; + _platform_console.vga[i + 1] = _platform_console.attribute; + } + + _platform_console.line = 0; + _platform_console.column = 0; +} + +/* + * this function scrolls the screen by copying the given number of lines + * from the bottom to the top. + * + * finally the function reinitializes the attributes to reflect the + * scrolling. + */ + +void platform_console_scroll(t_uint16 lines) +{ + t_uint16 src; + t_uint16 dst; + + for (src = lines * PLATFORM_CONSOLE_COLUMNS * PLATFORM_CONSOLE_BPC, dst = 0; + src < PLATFORM_CONSOLE_SIZE; + src++, dst++) + _platform_console.vga[dst] = _platform_console.vga[src]; + + for (src = (PLATFORM_CONSOLE_LINES - lines) * + PLATFORM_CONSOLE_COLUMNS * PLATFORM_CONSOLE_BPC; + src < PLATFORM_CONSOLE_SIZE; + src += 2) + { + _platform_console.vga[src + 0] = 0; + _platform_console.vga[src + 1] = _platform_console.attribute; + } + + _platform_console.line -= lines; + _platform_console.column = 0; +} + +/* + * this function changes the current console attributes. + */ + +void platform_console_attribute(t_uint8 attribute) +{ + _platform_console.attribute = attribute; +} + +/* + * this function prints a single character. + * + * first, should the number of lines exceed the maximum capacity, the screen + * is scrolled for a single line. + * + * the character is recorded at the cursor's current location while the + * following byte is filled with the current console's attributes. + * + * steps: + * + * 1) scroll the screen if necessary. + * 2) handle the '\n' character. + * 3) handle the '\r' character. + * 4) handle the '\t' character. + * 5) insert the character in the console's mapped buffer. + */ + +void platform_console_character(char c) +{ + t_uint16 pos; + + /* + * 1) + */ + + if (_platform_console.line >= PLATFORM_CONSOLE_LINES) + { + platform_console_scroll(1); + } + + /* + * 2) + */ + + if (c == '\n') + { + _platform_console.line++; + _platform_console.column = 0; + + return; + } + + /* + * 3) + */ + + if (c == '\r') + { + _platform_console.column = 0; + + return; + } + + /* + * 4) + */ + + if (c == '\t') + { + _platform_console.column += 8; + + if (_platform_console.column & 0x7) + _platform_console.column = _platform_console.column & ~0x7; + + return; + } + + /* + * 5) + */ + + if (_platform_console.column >= PLATFORM_CONSOLE_COLUMNS) + { + _platform_console.column = 0; + + ++_platform_console.line; + + if (_platform_console.line >= PLATFORM_CONSOLE_LINES) + platform_console_scroll(1); + } + + pos = _platform_console.line * + PLATFORM_CONSOLE_COLUMNS * PLATFORM_CONSOLE_BPC + + _platform_console.column * PLATFORM_CONSOLE_BPC; + + _platform_console.vga[pos] = c; + _platform_console.vga[pos + 1] = _platform_console.attribute; + + _platform_console.column++; +} + +/* + * this function displays a string, character by character. + */ + +void platform_console_string(char* string) +{ + t_uint32 i; + + for (i = 0; string[i] != '\0'; i++) + platform_console_character(string[i]); +} + +/* + * this function just initializes the console. + * + * steps: + * + * 1) initialize the manager's structure memory. + * 2) set the default attributes. + * 3) points the internal buffer to the device's mapped memory. + * 4) to start with, clear the console. + */ + +t_error platform_console_initialize(void) +{ + /* + * 1) + */ + + memset(&_platform_console, 0x0, sizeof (pm_console)); + + /* + * 2) + */ + + _platform_console.attribute = + PLATFORM_CONSOLE_FRONT(PLATFORM_CONSOLE_WHITE) | + PLATFORM_CONSOLE_BACK(PLATFORM_CONSOLE_BLACK) | + PLATFORM_CONSOLE_BRIGHT; + + /* + * 3) + */ + + _platform_console.vga = (char*)PLATFORM_CONSOLE_ADDRESS; + + /* + * 4) + */ + + platform_console_clear(); + + MACHINE_LEAVE(); +} + +/* + * this function just reinitializes the bootloader console. + * + * there is nothing special to do. + */ + +t_error platform_console_clean(void) +{ + MACHINE_LEAVE(); +} diff --git a/kaneton/machine/platform/ibm-pc/include/console.h b/kaneton/machine/platform/ibm-pc/include/console.h new file mode 100644 index 0000000..af7992f --- /dev/null +++ b/kaneton/machine/platform/ibm-pc/include/console.h @@ -0,0 +1,108 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...achine/platform/ibm-pc/include/console.h + * + * created julien quintard [wed jun 6 15:53:35 2007] + * updated julien quintard [thu dec 9 15:20:37 2010] + */ + +#ifndef PLATFORM_CONSOLE_H +#define PLATFORM_CONSOLE_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +#include + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * definitions related to the hardware console. + */ + +#define PLATFORM_CONSOLE_ADDRESS 0xb8000 +#define PLATFORM_CONSOLE_SIZE 0xfa0 +#define PLATFORM_CONSOLE_LINES 25 +#define PLATFORM_CONSOLE_COLUMNS 80 +#define PLATFORM_CONSOLE_BPC 2 /* bytes per character */ +#define PLATFORM_CONSOLE_TABULATION 8 + +#define PLATFORM_CONSOLE_FLICKER (1 << 7) +#define PLATFORM_CONSOLE_BRIGHT (1 << 3) + +#define PLATFORM_CONSOLE_BLACK 0x0 +#define PLATFORM_CONSOLE_BLUE 0x1 +#define PLATFORM_CONSOLE_GREEN 0x2 +#define PLATFORM_CONSOLE_CYAN 0x3 +#define PLATFORM_CONSOLE_RED 0x4 +#define PLATFORM_CONSOLE_MAGENTA 0x5 +#define PLATFORM_CONSOLE_YELLOW 0x6 +#define PLATFORM_CONSOLE_WHITE 0x7 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * macro-functions for easily setting the front and background colors. + */ + +#define PLATFORM_CONSOLE_FRONT(_color_) (_color_) +#define PLATFORM_CONSOLE_BACK(_color_) (_color_ << 4) + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * the console manager. + */ + +typedef struct +{ + t_uint16 line; + t_uint16 column; + t_uint8 attribute; + char* vga; +} pm_console; + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../console.c + */ + +/* + * ../console.c + */ + +void platform_console_clear(void); + +void platform_console_scroll(t_uint16 lines); + +void platform_console_attribute(t_uint8 attribute); + +void platform_console_character(char c); + +void platform_console_string(char* string); + +t_error platform_console_initialize(void); + +t_error platform_console_clean(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/platform/ibm-pc/include/pic.h b/kaneton/machine/platform/ibm-pc/include/pic.h new file mode 100644 index 0000000..0528aa3 --- /dev/null +++ b/kaneton/machine/platform/ibm-pc/include/pic.h @@ -0,0 +1,75 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...on/machine/platform/ibm-pc/include/pic.h + * + * created renaud voltz [wed mar 15 10:49:45 2006] + * updated julien quintard [fri jan 14 20:09:41 2011] + */ + +#ifndef PLATFORM_PIC_H +#define PLATFORM_PIC_H 1 + +/* + * ---------- defines --------------------------------------------------------- + */ + +/* + * pics addresses + */ + +#define PLATFORM_PIC_MASTER_PORT_A 0x20 +#define PLATFORM_PIC_MASTER_PORT_B 0x21 + +#define PLATFORM_PIC_SLAVE_PORT_A 0xa0 +#define PLATFORM_PIC_SLAVE_PORT_B 0xa1 + +/* + * master icw's + */ + +#define PLATFORM_PIC_MASTER_ICW_1 0x11 +#define PLATFORM_PIC_MASTER_ICW_2 0x20 +#define PLATFORM_PIC_MASTER_ICW_3 0x04 +#define PLATFORM_PIC_MASTER_ICW_4 0x01 + +/* + * slave icw's + */ + +#define PLATFORM_PIC_SLAVE_ICW_1 0x11 +#define PLATFORM_PIC_SLAVE_ICW_2 0x28 +#define PLATFORM_PIC_SLAVE_ICW_3 0x02 +#define PLATFORM_PIC_SLAVE_ICW_4 0x01 + + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../pic.c + */ + +/* + * ../pic.c + */ + +t_error platform_pic_enable(t_uint8 irq); + +t_error platform_pic_disable(t_uint8 irq); + +t_error platform_pic_acknowledge(t_uint8 irq); + +t_error platform_pic_initialize(void); + +t_error platform_pic_clean(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/platform/ibm-pc/include/pit.h b/kaneton/machine/platform/ibm-pc/include/pit.h new file mode 100644 index 0000000..09d807d --- /dev/null +++ b/kaneton/machine/platform/ibm-pc/include/pit.h @@ -0,0 +1,57 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...on/machine/platform/ibm-pc/include/pit.h + * + * created renaud voltz [wed mar 15 10:59:38 2006] + * updated julien quintard [fri jan 14 20:09:53 2011] + */ + +#ifndef PLATFORM_PIT_H +#define PLATFORM_PIT_H 1 + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * PIT ports + */ + +#define PLATFORM_PIT_0 0x40 +#define PLATFORM_PIT_1 0x41 +#define PLATFORM_PIT_2 0x42 + +#define PLATFORM_PIT_8254_CTRL 0x43 + +/* + * PIT oscillator frequency + */ + +#define PLATFORM_PIT_RATE 1193180 + + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../pit.c + */ + +/* + * ../pit.c + */ + +t_error platform_pit_initialize(void); + +t_error platform_pit_clean(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/platform/ibm-pc/include/platform.h b/kaneton/machine/platform/ibm-pc/include/platform.h new file mode 100644 index 0000000..fab89b9 --- /dev/null +++ b/kaneton/machine/platform/ibm-pc/include/platform.h @@ -0,0 +1,37 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...chine/platform/ibm-pc/include/platform.h + * + * created julien quintard [mon jun 11 05:59:19 2007] + * updated julien quintard [thu dec 9 14:37:15 2010] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file is the ibmpc main platform header. + */ + +#ifndef PLATFORM_PLATFORM_H +#define PLATFORM_PLATFORM_H 1 + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#endif diff --git a/kaneton/machine/platform/ibm-pc/include/rtc.h b/kaneton/machine/platform/ibm-pc/include/rtc.h new file mode 100644 index 0000000..ae2ca4f --- /dev/null +++ b/kaneton/machine/platform/ibm-pc/include/rtc.h @@ -0,0 +1,117 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...on/machine/platform/ibm-pc/include/rtc.h + * + * created julien quintard [wed nov 24 12:48:27 2010] + * updated julien quintard [thu dec 9 15:22:26 2010] + */ + +#ifndef PLATFORM_RTC_H +#define PLATFORM_RTC_H 1 + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * these macros provides the address, registers, options and flags + * related to the RTC. + */ + +#define PLATFORM_RTC_ADDRESS 0x70 +#define PLATFORM_RTC_DATA 0x71 + +#define PLATFORM_RTC_REGISTER_SECOND 0x00 +#define PLATFORM_RTC_REGISTER_MINUTE 0x02 +#define PLATFORM_RTC_REGISTER_HOUR 0x04 +#define PLATFORM_RTC_REGISTER_WEEKDAY 0x06 +#define PLATFORM_RTC_REGISTER_DAY_OF_MONTH 0x07 +#define PLATFORM_RTC_REGISTER_MONTH 0x08 +#define PLATFORM_RTC_REGISTER_YEAR 0x09 +#define PLATFORM_RTC_REGISTER_STATUS_A 0x0a +#define PLATFORM_RTC_REGISTER_STATUS_B 0x0b +#define PLATFORM_RTC_REGISTER_CENTURY 0x32 + +#define PLATFORM_RTC_FORMAT_24H 0x02 +#define PLATFORM_RTC_FORMAT_BCD 0x04 + +#define PLATFORM_RTC_OPTION_DST 0x01 /* daylight saving time */ + +#define PLATFORM_RTC_FORMAT PLATFORM_RTC_FORMAT_BCD + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * this structure represents the current state of the RTC. + */ + +typedef struct +{ + t_uint32 millisecond; + + t_uint8 second; + t_uint8 minute; + t_uint8 hour; + + t_uint8 day; + t_uint8 month; + t_uint8 year; + + t_uint8 weekday; + t_uint8 century; +} ps_rtc_state; + +/* + * the RTC manager. + */ + +typedef struct +{ + ps_rtc_state state; +} pm_rtc; + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../rtc.c + * + */ + +/* + * ../rtc.c + */ + +void platform_rtc_dump(ps_rtc_state* rtc); + +t_error platform_rtc_write(t_uint8 address, + t_uint8 value); + +t_error platform_rtc_read(t_uint8 address, + t_uint8* value); + +t_error platform_rtc_extract(t_uint8 address, + t_uint8* value); + +t_error platform_rtc_load(ps_rtc_state* rtc); + +t_error platform_rtc_state(ps_rtc_state** rtc); + +t_error platform_rtc_update(t_uint32 millisecond); + +t_error platform_rtc_initialize(void); + +t_error platform_rtc_clean(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/platform/ibm-pc/include/serial.h b/kaneton/machine/platform/ibm-pc/include/serial.h new file mode 100644 index 0000000..4ee16a4 --- /dev/null +++ b/kaneton/machine/platform/ibm-pc/include/serial.h @@ -0,0 +1,84 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...machine/platform/ibm-pc/include/serial.h + * + * created julien quintard [wed jun 6 14:06:42 2007] + * updated julien quintard [thu dec 9 15:26:34 2010] + */ + +#ifndef PLATFORM_SERIAL_H +#define PLATFORM_SERIAL_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * these macro provide the addresses and options related to the serial + * device. + */ + +#define PLATFORM_SERIAL_PRIMARY 0x3f8 +#define PLATFORM_SERIAL_SECONDARY 0x2f8 +#define PLATFORM_SERIAL_COM_3 0x3e8 +#define PLATFORM_SERIAL_COM_4 0x2e8 + +#define PLATFORM_SERIAL_BR9600 0x0C +#define PLATFORM_SERIAL_BR19200 0x06 +#define PLATFORM_SERIAL_BR38400 0x03 +#define PLATFORM_SERIAL_BR57600 0x02 +#define PLATFORM_SERIAL_BR115200 0x01 + +#define PLATFORM_SERIAL_8N1 0x03 +#define PLATFORM_SERIAL_7N1 0x02 +#define PLATFORM_SERIAL_8N2 0x07 +#define PLATFORM_SERIAL_7N2 0x06 + +#define PLATFORM_SERIAL_FIFO_14 0xC7 +#define PLATFORM_SERIAL_FIFO_8 0x87 +#define PLATFORM_SERIAL_FIFO_4 0x47 +#define PLATFORM_SERIAL_FIFO_1 0x07 + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../serial.c + */ + +/* + * ../serial.c + */ + +void platform_serial_read(t_uint32 port, + t_uint8* data, + t_uint32 size); + +void platform_serial_write(t_uint32 port, + t_uint8* data, + t_uint32 size); + +t_error platform_serial_setup(t_uint32 port, + t_uint8 rate, + t_uint8 type); + +t_error platform_serial_initialize(void); + +t_error platform_serial_clean(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/machine/platform/ibm-pc/pic.c b/kaneton/machine/platform/ibm-pc/pic.c new file mode 100644 index 0000000..3bcfb16 --- /dev/null +++ b/kaneton/machine/platform/ibm-pc/pic.c @@ -0,0 +1,254 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/machine/platform/ibm-pc/pic.c + * + * created matthieu bucchianeri [tue dec 20 13:44:58 2005] + * updated julien quintard [fri jan 14 20:10:47 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file implements the management of the PIC - Programmable Interrupt + * Controller 8259A on ibmpc. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +#include +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * turn on the given interrupt line by enabling its flag in the appropriate + * PIC i.e master or slave. + * + * steps: + * + * 1) check IRQ bounds. + * 2) enable to flag associated with the given IRQ. + */ + +t_error platform_pic_enable(t_uint8 irq) +{ + t_uint8 mask; + + /* + * 1) + */ + + if (irq > 15) + MACHINE_ESCAPE("the irq number '%u' is invalid", + irq); + + /* + * 2) + */ + + if (irq < 8) + { + ARCHITECTURE_IO_IN_8(PLATFORM_PIC_MASTER_PORT_B, + mask); + + ARCHITECTURE_IO_OUT_8(PLATFORM_PIC_MASTER_PORT_B, + mask & ~(1 << irq)); + } + else + { + ARCHITECTURE_IO_IN_8(PLATFORM_PIC_SLAVE_PORT_B, + mask); + + ARCHITECTURE_IO_OUT_8(PLATFORM_PIC_SLAVE_PORT_B, + mask & ~(1 << (irq - 8))); + } + + MACHINE_LEAVE(); +} + +/* + * turn off the given interrupt following the process for enabling IRQs. + * + * steps: + * + * 1) check IRQ bounds. + * 2) disable to flag associated with the given IRQ. + */ + +t_error platform_pic_disable(t_uint8 irq) +{ + t_uint8 mask; + + /* + * 1) + */ + + if (irq > 15) + MACHINE_ESCAPE("the irq number '%u' is invalid", + irq); + + /* + * 2) + */ + + if (irq < 8) + { + ARCHITECTURE_IO_IN_8(PLATFORM_PIC_MASTER_PORT_B, + mask); + + ARCHITECTURE_IO_OUT_8(PLATFORM_PIC_MASTER_PORT_B, + mask | (1 << irq)); + } + else + { + ARCHITECTURE_IO_IN_8(PLATFORM_PIC_SLAVE_PORT_B, + mask); + + ARCHITECTURE_IO_OUT_8(PLATFORM_PIC_SLAVE_PORT_B, + mask | (1 << (irq - 8))); + } + + MACHINE_LEAVE(); +} + +/* + * acknowlegde the appropriate PIC i.e master or slave for the given IRQ. + * + * steps: + * + * 1) check IRQ bounds. + * 2) send the EOI - End Of Interrupt signal to the proper PIC. + */ + +t_error platform_pic_acknowledge(t_uint8 irq) +{ + t_uint8 mask; + + /* + * 1) + */ + + if (irq > 15) + MACHINE_ESCAPE("the irq number '%u' is invalid", + irq); + + /* + * 2) + */ + + if (irq < 8) + { + ARCHITECTURE_IO_IN_8(PLATFORM_PIC_MASTER_PORT_B, + mask); + + ARCHITECTURE_IO_OUT_8(PLATFORM_PIC_MASTER_PORT_B, + mask); + + ARCHITECTURE_IO_OUT_8(PLATFORM_PIC_MASTER_PORT_A, + 0x60 + irq); + } + else + { + ARCHITECTURE_IO_IN_8(PLATFORM_PIC_SLAVE_PORT_B, + mask); + + ARCHITECTURE_IO_OUT_8(PLATFORM_PIC_SLAVE_PORT_B, + mask); + + ARCHITECTURE_IO_OUT_8(PLATFORM_PIC_SLAVE_PORT_A, + 0x60 + (irq - 8)); + + ARCHITECTURE_IO_OUT_8(PLATFORM_PIC_MASTER_PORT_A, + 0x60 + 2); + } + + MACHINE_LEAVE(); +} + +/* + * initialize the PIC manager. + * + * steps: + * + * 1) send ICW1. + * 2) send ICW2. + * 3) send ICW3. + * 4) send ICW4. + * 5) all interrupts are set off at initialization. + * + */ + +t_error platform_pic_initialize(void) +{ + /* + * 1) + */ + + ARCHITECTURE_IO_OUT_8(PLATFORM_PIC_MASTER_PORT_A, + PLATFORM_PIC_MASTER_ICW_1); + + ARCHITECTURE_IO_OUT_8(PLATFORM_PIC_SLAVE_PORT_A, + PLATFORM_PIC_SLAVE_ICW_1); + + /* + * 2) + */ + + ARCHITECTURE_IO_OUT_8(PLATFORM_PIC_MASTER_PORT_B, + PLATFORM_PIC_MASTER_ICW_2); + + ARCHITECTURE_IO_OUT_8(PLATFORM_PIC_SLAVE_PORT_B, + PLATFORM_PIC_SLAVE_ICW_2); + + /* + * 3) + */ + + ARCHITECTURE_IO_OUT_8(PLATFORM_PIC_MASTER_PORT_B, + PLATFORM_PIC_MASTER_ICW_3); + + ARCHITECTURE_IO_OUT_8(PLATFORM_PIC_SLAVE_PORT_B, + PLATFORM_PIC_SLAVE_ICW_3); + + /* + * 4) + */ + + ARCHITECTURE_IO_OUT_8(PLATFORM_PIC_MASTER_PORT_B, + PLATFORM_PIC_MASTER_ICW_4); + + ARCHITECTURE_IO_OUT_8(PLATFORM_PIC_SLAVE_PORT_B, + PLATFORM_PIC_SLAVE_ICW_4); + + /* + * 5) + */ + + ARCHITECTURE_IO_OUT_8(PLATFORM_PIC_MASTER_PORT_B, + 0xfb); + + ARCHITECTURE_IO_OUT_8(PLATFORM_PIC_SLAVE_PORT_B, + 0xff); + + MACHINE_LEAVE(); +} + +/* + * clean the PIC manager. + */ + +t_error platform_pic_clean(void) +{ + MACHINE_LEAVE(); +} diff --git a/kaneton/machine/platform/ibm-pc/pit.c b/kaneton/machine/platform/ibm-pc/pit.c new file mode 100644 index 0000000..2caf57b --- /dev/null +++ b/kaneton/machine/platform/ibm-pc/pit.c @@ -0,0 +1,87 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/machine/platform/ibm-pc/pit.c + * + * created renaud voltz [mon feb 27 04:20:33 2006] + * updated julien quintard [fri jan 14 20:12:22 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file implements the management of the PIT - Programmable Interval + * Timer 8254 on ibmpc. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +#include +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * initialize the PIT 8254. + * + * steps: + * + * 1) calculate and check counter limit for the given frequency. + * 2) the PIT 8254 interprets value 0 as 65536. + * 3) setup the PIT0 to run in mode 2 (rate generator). + */ + +t_error platform_pit_initialize(void) +{ + t_uint32 latch; + + /* + * 1) + */ + + latch = PLATFORM_PIT_RATE / (1000 / TIMER_DELAY); + + if (latch == 0 || latch > 65536) + MACHINE_ESCAPE("the computed latch is invalid"); + + /* + * 2) + */ + + if (latch == 65536) + latch = 0; + + /* + * 3) + */ + + ARCHITECTURE_IO_OUT_8(PLATFORM_PIT_8254_CTRL, + 0x34); + + ARCHITECTURE_IO_OUT_8(PLATFORM_PIT_0, + latch & 0xFF); + + ARCHITECTURE_IO_OUT_8(PLATFORM_PIT_0, + (latch >> 8) & 0xFF); + + MACHINE_LEAVE(); +} + +/* + * this function cleans the PIT. + */ + +t_error platform_pit_clean(void) +{ + MACHINE_LEAVE(); +} diff --git a/kaneton/machine/platform/ibm-pc/rtc.c b/kaneton/machine/platform/ibm-pc/rtc.c new file mode 100644 index 0000000..a43c973 --- /dev/null +++ b/kaneton/machine/platform/ibm-pc/rtc.c @@ -0,0 +1,309 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/machine/platform/ibm-pc/rtc.c + * + * created julien quintard [wed nov 24 10:17:08 2010] + * updated julien quintard [sun jan 30 13:01:05 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file provides functionalities related to the RTC - Real-Time Clock. + * + * although there are many ways to manage the date and time, the solution + * chosen here consists in: (i) initially, read the date and time from the + * CMOS/RTC, (ii) rely on the PIT's interrupt---which occur every + * TIMER_DELAY---in order to keep track of time. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +#include + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the RTC variable. + */ + +pm_rtc _platform_rtc; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function dumps the RTC structure's attributes. + */ + +void platform_rtc_dump(ps_rtc_state* rtc) +{ + module_call(console, message, + '#', "dumping the RTC structure:\n"); + + module_call(console, message, + '#', " millisecond: %u\n", + rtc->millisecond); + + module_call(console, message, + '#', " second: %u\n", + rtc->second); + + module_call(console, message, + '#', " minute: %u\n", + rtc->minute); + + module_call(console, message, + '#', " hour: %u\n", + rtc->hour); + + module_call(console, message, + '#', " day: %u\n", + rtc->day); + + module_call(console, message, + '#', " month: %u\n", + rtc->month); + + module_call(console, message, + '#', " year: %u\n", + rtc->year); + + module_call(console, message, + '#', " week day: %u\n", + rtc->weekday); + + module_call(console, message, + '#', " century: %u\n", + rtc->century); +} + +/* + * this function updates a register of the RTC. + */ + +t_error platform_rtc_write(t_uint8 address, + t_uint8 value) +{ + ARCHITECTURE_IO_OUT_8(PLATFORM_RTC_ADDRESS, address); + ARCHITECTURE_IO_OUT_8(PLATFORM_RTC_DATA, value); + + MACHINE_LEAVE(); +} + +/* + * this function fetches the value of the given RTC register. + */ + +t_error platform_rtc_read(t_uint8 address, + t_uint8* value) +{ + ARCHITECTURE_IO_OUT_8(PLATFORM_RTC_ADDRESS, address); + ARCHITECTURE_IO_IN_8(PLATFORM_RTC_DATA, *value); + + MACHINE_LEAVE(); +} + +/* + * this function reads a RTC register and converts it into + * binary so that the caller can use it. + */ + +t_error platform_rtc_extract(t_uint8 address, + t_uint8* value) +{ + switch (PLATFORM_RTC_FORMAT) + { + case PLATFORM_RTC_FORMAT_BCD: + { + t_uint8 bcd; + + if (platform_rtc_read(address, &bcd) != ERROR_OK) + MACHINE_ESCAPE("unable to read the RTC"); + + *value = (bcd >> 1) + (bcd >> 3) + (bcd & 0xf); + + break; + } + case PLATFORM_RTC_FORMAT_24H: + default: + { + MACHINE_ESCAPE("unsupported format '%u'", + PLATFORM_RTC_FORMAT); + } + } + + MACHINE_LEAVE(); +} + +/* + * this function reads all the registers composing the RTC + * structure. + * + * note however that since the RTC does not handle milliseconds, the + * milliseconds attribute is initially set to zero. + */ + +t_error platform_rtc_load(ps_rtc_state* rtc) +{ + rtc->millisecond = 0; + + if (platform_rtc_extract(PLATFORM_RTC_REGISTER_SECOND, + &rtc->second) != ERROR_OK) + MACHINE_ESCAPE("unable to extract the RTC's seconds"); + + if (platform_rtc_extract(PLATFORM_RTC_REGISTER_MINUTE, + &rtc->minute) != ERROR_OK) + MACHINE_ESCAPE("unable to extract the RTC's minutes"); + + if (platform_rtc_extract(PLATFORM_RTC_REGISTER_HOUR, + &rtc->hour) != ERROR_OK) + MACHINE_ESCAPE("unable to extract the RTC's hours"); + + if (platform_rtc_extract(PLATFORM_RTC_REGISTER_DAY_OF_MONTH, + &rtc->day) != ERROR_OK) + MACHINE_ESCAPE("unable to extract the RTC's days"); + + if (platform_rtc_extract(PLATFORM_RTC_REGISTER_MONTH, + &rtc->month) != ERROR_OK) + MACHINE_ESCAPE("unable to extract the RTC's months"); + + if (platform_rtc_extract(PLATFORM_RTC_REGISTER_YEAR, + &rtc->year) != ERROR_OK) + MACHINE_ESCAPE("unable to extract the RTC's years"); + + if (platform_rtc_extract(PLATFORM_RTC_REGISTER_WEEKDAY, + &rtc->weekday) != ERROR_OK) + MACHINE_ESCAPE("unable to extract the RTC's weekday"); + + if (platform_rtc_extract(PLATFORM_RTC_REGISTER_CENTURY, + &rtc->century) != ERROR_OK) + MACHINE_ESCAPE("unable to extract the RTC's century"); + + MACHINE_LEAVE(); +} + +/* + * this function returns a pointer on the current RTC state structure. + */ + +t_error platform_rtc_state(ps_rtc_state** rtc) +{ + *rtc = &_platform_rtc.state; + + MACHINE_LEAVE(); +} + +/* + * this function increases the number of milliseconds and possibly updates the + * RTC structure. + * + * steps: + * + * 1) increase the number of milliseconds. + * 2) adjust the attributes since the number of seconds + * can exceed 60 for instance, and so on for the other + * attributes. + */ + +t_error platform_rtc_update(t_uint32 millisecond) +{ + /* + * 1) + */ + + _platform_rtc.state.millisecond += millisecond; + + /* + * 2) + */ + + if (_platform_rtc.state.millisecond > 1000) + { + _platform_rtc.state.second += _platform_rtc.state.millisecond / 1000; + _platform_rtc.state.millisecond = _platform_rtc.state.millisecond % 1000; + } + + if (_platform_rtc.state.second > 60) + { + _platform_rtc.state.minute += _platform_rtc.state.second / 60; + _platform_rtc.state.second = _platform_rtc.state.second % 60; + } + + if (_platform_rtc.state.minute > 60) + { + _platform_rtc.state.hour += _platform_rtc.state.minute / 60; + _platform_rtc.state.minute = _platform_rtc.state.minute % 60; + } + + if (_platform_rtc.state.hour > 24) + { + _platform_rtc.state.day += _platform_rtc.state.hour / 24; + _platform_rtc.state.hour = _platform_rtc.state.hour % 24; + } + + MACHINE_LEAVE(); +} + +/* + * this function initializes the RTC with several options. + * + * steps: + * + * 1) initialize the manager's structure. + * 2) activate the BCD format. + * 3) load the initial RTC. + */ + +t_error platform_rtc_initialize(void) +{ + t_uint8 status; + + /* + * 1) + */ + + memset(&_platform_rtc, 0x0, sizeof (pm_rtc)); + + /* + * 2) + */ + + if (platform_rtc_read(PLATFORM_RTC_REGISTER_STATUS_B, &status) != ERROR_OK) + MACHINE_ESCAPE("unable to read the RTC"); + + if (platform_rtc_write(PLATFORM_RTC_REGISTER_STATUS_B, + status | + PLATFORM_RTC_FORMAT_BCD) != ERROR_OK) + MACHINE_ESCAPE("unable to write the RTC"); + + /* + * 2) + */ + + if (platform_rtc_load(&_platform_rtc.state) != ERROR_OK) + MACHINE_ESCAPE("unable to load the initial RTC"); + + MACHINE_LEAVE(); +} + +/* + * this function cleans the RTC. + */ + +t_error platform_rtc_clean(void) +{ + MACHINE_LEAVE(); +} diff --git a/kaneton/machine/platform/ibm-pc/serial.c b/kaneton/machine/platform/ibm-pc/serial.c new file mode 100644 index 0000000..66f4d85 --- /dev/null +++ b/kaneton/machine/platform/ibm-pc/serial.c @@ -0,0 +1,199 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...kaneton/machine/platform/ibm-pc/serial.c + * + * created julien quintard [sat may 28 18:23:13 2005] + * updated julien quintard [fri jan 14 20:13:12 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file provides functionalities for sending and receiving data + * to/from a serial line. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +#include + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function reads on the given serial device. + * + * steps: + * + * 1) until enough bytes have been read. + * a) read the status register. + * b) if the data is ready to be read, read the register. + */ + +void platform_serial_read(t_uint32 port, + t_uint8* data, + t_uint32 size) +{ + t_uint8 status; + + /* + * 1) + */ + + while (size) + { + /* + * a) + */ + + ARCHITECTURE_IO_IN_8(port + 5, + status); + + /* + * b) + */ + + if (status & 1) + { + ARCHITECTURE_IO_IN_8(port, + *data++); + + size--; + } + } +} + +/* + * this function writes on the serial device. + * + * 1) until enough bytes have been written. + * a) read the status register. + * b) if the register is ready to be written, write it. + */ + +void platform_serial_write(t_uint32 port, + t_uint8* data, + t_uint32 size) +{ + t_uint8 status; + + /* + * 1) + */ + + while(size) + { + /* + * a) + */ + + ARCHITECTURE_IO_IN_8(port + 5, + status); + + /* + * b) + */ + + if (status & 0x20) + { + ARCHITECTURE_IO_OUT_8(port, + *data++); + + size--; + } + } +} + +/* + * this function sets up the serial port. + * + * steps: + * + * 1) disable IRQs. + * 2) enable the baud rate divisor. + * 3) set the baude rate divisor. + * 4) set the serial type such as 8 bits, no parity and one bit stop which + * is referred to as 8N1. + * 5) enable the FIFO. + * 6) re-enable IRQs. + */ + +t_error platform_serial_setup(t_uint32 port, + t_uint8 rate, + t_uint8 type) +{ + /* + * 1) + */ + + ARCHITECTURE_IO_OUT_8(port + 1, + 0x00); + + /* + * 2) + */ + + ARCHITECTURE_IO_OUT_8(port + 3, + 0x80); + + /* + * 3) + */ + + ARCHITECTURE_IO_OUT_8(port + 0, + rate); + + ARCHITECTURE_IO_OUT_8(port + 1, + 0x00); + + /* + * 4) + */ + + ARCHITECTURE_IO_OUT_8(port + 3, + type); + + /* + * 5) + */ + + ARCHITECTURE_IO_OUT_8(port + 2, + PLATFORM_SERIAL_FIFO_8); + + /* + * 6) + */ + + ARCHITECTURE_IO_OUT_8(port + 4, + 0x08); + + MACHINE_LEAVE(); +} + +/* + * this function initializes the serial manager. + */ + +t_error platform_serial_initialize(void) +{ + MACHINE_LEAVE(); +} + +/* + * this function cleans the serial manager. + */ + +t_error platform_serial_clean(void) +{ + MACHINE_LEAVE(); +} diff --git a/kaneton/modules/Makefile b/kaneton/modules/Makefile new file mode 100644 index 0000000..f881867 --- /dev/null +++ b/kaneton/modules/Makefile @@ -0,0 +1,84 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/kaneton/modules/Makefile +# +# created matthieu bucchianeri [sat sep 1 12:40:51 2007] +# updated julien quintard [fri dec 10 14:25:46 2010] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/modules + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers + +# +# ---------- variables -------------------------------------------------------- +# + +SUBDIRS := bundle \ + console \ + forward \ + report \ + test + +MODULES_C := modules.c +MODULES_O := modules.o + +MODULES_LO := $(foreach m,$(_MODULES_),$(m)/$(m).lo) + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(_MODULES_LO_) + +$(_MODULES_LO_): $(MODULES_O) $(MODULES_LO) + $(call env_remove,$(_MODULES_LO_),) + + $(call env_archive,$(_MODULES_LO_),$(MODULES_O) $(MODULES_LO),) + +clear: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,clear,) ; \ + done + + $(call env_remove,$(_MODULES_LO_),) + $(call env_remove,$(MODULES_O),) + + $(call env_purge,) + +prototypes: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,prototypes,) ; \ + done + +headers: + for d in $(_MODULES_) ; do \ + $(call env_launch,$${d}/Makefile,headers,) ; \ + done + +dependencies: + for d in $(_MODULES_) ; do \ + $(call env_launch,$${d}/Makefile,,) ; \ + done + +endif diff --git a/kaneton/modules/bundle/Makefile b/kaneton/modules/bundle/Makefile new file mode 100644 index 0000000..8e2a361 --- /dev/null +++ b/kaneton/modules/bundle/Makefile @@ -0,0 +1,68 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton.STABLE/kaneton/modules/bundle/Makefile +# +# created julien quintard [wed apr 15 07:00:50 2009] +# updated julien quintard [sat dec 4 23:35:47 2010] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/core/bundle + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +BUNDLE_LO := bundle.lo + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(BUNDLE_LO) + +$(BUNDLE_LO): $(_TEST_STORE_BUNDLE_KANETON_LO_) + $(call env_remove,$(BUNDLE_LO),) + + $(call env_archive,$(BUNDLE_LO),$(_TEST_STORE_BUNDLE_KANETON_LO_),) + +clear: + $(call env_remove,$(BUNDLE_LO),) + + $(call env_purge,) + +prototypes: + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + +dependencies: + + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/modules/console/Makefile b/kaneton/modules/console/Makefile new file mode 100644 index 0000000..2db9bc1 --- /dev/null +++ b/kaneton/modules/console/Makefile @@ -0,0 +1,78 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/kaneton/modules/console/Makefile +# +# created julien quintard [wed apr 15 07:00:50 2009] +# updated julien quintard [fri dec 10 14:19:12 2010] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/core/console + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +CONSOLE_LO := console.lo + +CONSOLE_C := console.c + +CONSOLE_O := $(CONSOLE_C:.c=.o) + +CONSOLE_INCLUDE := $(directory)/include/console.h + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(CONSOLE_LO) + +$(CONSOLE_LO): $(CONSOLE_O) + $(call env_remove,$(CONSOLE_LO),) + + $(call env_archive,$(CONSOLE_LO),$(CONSOLE_O),) + +clear: + $(call env_remove,$(CONSOLE_O),) + + $(call env_remove,$(CONSOLE_LO),) + + $(call env_purge,) + +prototypes: + $(call env_prototypes,$(CONSOLE_INCLUDE),) + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(CONSOLE_C),) + +dependencies: + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/modules/console/console.c b/kaneton/modules/console/console.c new file mode 100644 index 0000000..ab5f39f --- /dev/null +++ b/kaneton/modules/console/console.c @@ -0,0 +1,274 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/modules/console/console.c + * + * created matthieu bucchianeri [sat jun 16 18:10:38 2007] + * updated julien quintard [thu apr 7 15:13:01 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file implements the in-kernel console utility. + * + * this module is partially portable since it relies on the platform's console + * functionality. + */ + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the console module manager. + */ + +mm_console _module_console; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function is called by format() for displaying a character. + */ + +void module_console_character(char c) +{ + if (_module_console.character == NULL) + return; + + _module_console.character(c); +} + +/* + * this function is called by format() for setting the attribute. + */ + +void module_console_attribute(t_uint8 attribute) +{ + if (_module_console.attribute == NULL) + return; + + _module_console.attribute(attribute); +} + +/* + * this function displays an advanced message. + * + * steps: + * + * 1) display the opening bracket. + * 2) display the indicator in a specific color. + * 3) display the closing bracket. + * 4) display the space. + * 5) call the format() function to treat the arguments. + */ + +void module_console_message(char indicator, + char* fmt, + ...) +{ + va_list args; + + /* + * 1) + */ + + module_console_attribute(MODULE_CONSOLE_FRONT(MODULE_CONSOLE_BLUE) | + MODULE_CONSOLE_BACK(MODULE_CONSOLE_BLACK) | + MODULE_CONSOLE_BRIGHT); + + module_console_character('['); + + /* + * 2) + */ + + switch (indicator) + { + case '+': + module_console_attribute(MODULE_CONSOLE_FRONT(MODULE_CONSOLE_GREEN) | + MODULE_CONSOLE_BACK(MODULE_CONSOLE_BLACK) | + MODULE_CONSOLE_BRIGHT); + break; + case '#': + module_console_attribute(MODULE_CONSOLE_FRONT(MODULE_CONSOLE_MAGENTA) | + MODULE_CONSOLE_BACK(MODULE_CONSOLE_BLACK) | + MODULE_CONSOLE_BRIGHT); + break; + case '!': + module_console_attribute(MODULE_CONSOLE_FRONT(MODULE_CONSOLE_RED) | + MODULE_CONSOLE_BACK(MODULE_CONSOLE_BLACK) | + MODULE_CONSOLE_BRIGHT); + break; + } + + module_console_character(indicator); + + /* + * 3) + */ + + module_console_attribute(MODULE_CONSOLE_FRONT(MODULE_CONSOLE_BLUE) | + MODULE_CONSOLE_BACK(MODULE_CONSOLE_BLACK) | + MODULE_CONSOLE_BRIGHT); + + module_console_character(']'); + + /* + * 4) + */ + + module_console_attribute(MODULE_CONSOLE_FRONT(MODULE_CONSOLE_WHITE) | + MODULE_CONSOLE_BACK(MODULE_CONSOLE_BLACK) | + MODULE_CONSOLE_BRIGHT); + + module_console_character(' '); + + /* + * 5) + */ + + va_start(args, fmt); + + format(module_console_character, module_console_attribute, + fmt, args); + + va_end(args); +} + +/* + * this function prints a formatted text to the screen. + */ + +void module_console_print(char* fmt, + ...) +{ + va_list args; + + va_start(args, fmt); + + format(module_console_character, module_console_attribute, + fmt, args); + + va_end(args); +} + +/* + * this function sets the console configuration pointers. + */ + +t_error module_console_set(mf_console_character character, + mf_console_attribute attribute) +{ + _module_console.character = character; + _module_console.attribute = attribute; + + MODULE_LEAVE(); +} + +/* + * this function returns the console configuration pointers. + */ + +t_error module_console_get(mf_console_character* character, + mf_console_attribute* attribute) +{ + *character = _module_console.character; + *attribute = _module_console.attribute; + + MODULE_LEAVE(); +} + +/* + * this function loads the module. + * + * steps: + * + * 1) initialize the console structure. + * 2) initialize the platform console. + * 3) set the initialize configuration pointers to forward the calls + * to the platform. + * 4) initialize the console attribute. + * 5) display a message. + */ + +t_error module_console_load(void) +{ + /* + * 1) + */ + + memset(&_module_console, 0x0, sizeof (mm_console)); + + /* + * 2) + */ + + if (platform_console_initialize() != ERROR_OK) + MODULE_ESCAPE("unable to initialize the platform's console"); + + /* + * 3) + */ + + module_console_set(platform_console_character, + platform_console_attribute); + + /* + * 4) + */ + + module_console_attribute(MODULE_CONSOLE_FRONT(MODULE_CONSOLE_WHITE) | + MODULE_CONSOLE_BACK(MODULE_CONSOLE_BLACK) | + MODULE_CONSOLE_BRIGHT); + + /* + * 5) + */ + + module_call(console, message, + '+', "loading the 'console' module\n"); + + MODULE_LEAVE(); +} + +/* + * this function unloads the module. + * + * steps: + * + * 1) display a message. + * 2) clean the underlying platform console. + */ + +t_error module_console_unload(void) +{ + /* + * 1) + */ + + module_call(console, message, + '+', "unloading the 'console' module\n"); + + /* + * 2) + */ + + if (platform_console_clean() != ERROR_OK) + MODULE_ESCAPE("unable to clean the platform's console"); + + MODULE_LEAVE(); +} diff --git a/kaneton/modules/console/include/console.h b/kaneton/modules/console/include/console.h new file mode 100644 index 0000000..9fc3099 --- /dev/null +++ b/kaneton/modules/console/include/console.h @@ -0,0 +1,132 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...aneton/modules/console/include/console.h + * + * created julien quintard [wed jun 6 16:25:44 2007] + * updated julien quintard [sun dec 12 13:49:44 2010] + */ + +#ifndef MODULES_CONSOLE_CONSOLE_H +#define MODULES_CONSOLE_CONSOLE_H 1 + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * the print function pointer. + */ + +typedef void (*mf_console_character)(char); +typedef void (*mf_console_attribute)(t_uint8); + +/* + * the console manager. + */ + +typedef struct +{ + mf_console_character character; + mf_console_attribute attribute; +} mm_console; + +/* + * this type represent the length of the margin the generate before + * displaying some text. + */ + +typedef t_uint8 mt_margin; + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * since the console module relies on the platform when it comes to the + * default behaviour, the following macros map the platform ones. + */ + +#define MODULE_CONSOLE_FLICKER PLATFORM_CONSOLE_FLICKER +#define MODULE_CONSOLE_BRIGHT PLATFORM_CONSOLE_BRIGHT + +#define MODULE_CONSOLE_BLACK PLATFORM_CONSOLE_BLACK +#define MODULE_CONSOLE_BLUE PLATFORM_CONSOLE_BLUE +#define MODULE_CONSOLE_GREEN PLATFORM_CONSOLE_GREEN +#define MODULE_CONSOLE_CYAN PLATFORM_CONSOLE_CYAN +#define MODULE_CONSOLE_RED PLATFORM_CONSOLE_RED +#define MODULE_CONSOLE_MAGENTA PLATFORM_CONSOLE_MAGENTA +#define MODULE_CONSOLE_YELLOW PLATFORM_CONSOLE_YELLOW +#define MODULE_CONSOLE_WHITE PLATFORM_CONSOLE_WHITE + +/* + * these macro provide (i) the format for generating a variable-length + * string of spaces (ii) the default shifting value for hierarchical + * display. + */ + +#define MODULE_CONSOLE_MARGIN_FORMAT "%*s" + +#define MODULE_CONSOLE_MARGIN_NONE 0 +#define MODULE_CONSOLE_MARGIN_SHIFT 2 + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * this macro-function generates two arguments for a format-based + * print function in order to generate a string composed of _values_ spaces. + */ + +#define MODULE_CONSOLE_MARGIN_VALUE(_value_) _value_, " " + +/* + * since the console module relies on the platform when it comes to the + * default behaviour, the following macro-functions map the platform ones. + */ + +#define MODULE_CONSOLE_FRONT(_color_) PLATFORM_CONSOLE_FRONT(_color_) +#define MODULE_CONSOLE_BACK(_color_) PLATFORM_CONSOLE_BACK(_color_) + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../console.c + */ + +/* + * ../console.c + */ + +void module_console_character(char c); + +void module_console_attribute(t_uint8 attribute); + +void module_console_message(char indicator, + char* fmt, + ...); + +void module_console_print(char* fmt, + ...); + +t_error module_console_set(mf_console_character character, + mf_console_attribute attribute); + +t_error module_console_get(mf_console_character* character, + mf_console_attribute* attribute); + +t_error module_console_load(void); + +t_error module_console_unload(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/modules/forward/Makefile b/kaneton/modules/forward/Makefile new file mode 100644 index 0000000..8e8c7d2 --- /dev/null +++ b/kaneton/modules/forward/Makefile @@ -0,0 +1,79 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/kaneton/modules/forward/Makefile +# +# created julien quintard [wed apr 15 07:00:50 2009] +# updated julien quintard [mon may 17 11:48:03 2010] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/core/forward + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +FORWARD_LO := forward.lo + +FORWARD_C := forward.c + +FORWARD_O := $(FORWARD_C:.c=.o) + +FORWARD_INCLUDE := $(directory)/include/forward.h + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(FORWARD_LO) + +$(FORWARD_LO): $(FORWARD_O) + $(call env_remove,$(FORWARD_LO),) + + $(call env_archive,$(FORWARD_LO),$(FORWARD_O),) + +clear: + $(call env_remove,$(FORWARD_O),) + + $(call env_remove,$(FORWARD_LO),) + + $(call env_purge,) + +prototypes: + + $(call env_prototypes,$(FORWARD_INCLUDE),) + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(FORWARD_C),) + +dependencies: + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/modules/forward/forward.c b/kaneton/modules/forward/forward.c new file mode 100644 index 0000000..54be50f --- /dev/null +++ b/kaneton/modules/forward/forward.c @@ -0,0 +1,215 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/modules/forward/forward.c + * + * created matthieu bucchianeri [sat jun 16 18:10:38 2007] + * updated julien quintard [thu apr 7 15:13:11 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file implements a module which forwards everything text displayed + * through the console module to the serial line. + * + * this module is only partially portable as it relies on the platform + * serial functionality. + */ + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#if !defined(MODULE_console) +# error "the 'forward' module depends upon the 'console' module" +#endif + +#if defined(MODULE_test) +# error "the 'test' module cannot be activated with the 'forward' module" +#endif + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the module manager. + */ + +mm_forward _module_forward; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function forces the module to send the buffered and given text to + * the serial line. + * + * steps: + * + * 1) flush the buffer hence forcing it to be sent to the serial line. + * 2) send the given message to the serial line. + */ + +t_error module_forward_send(char* message, + t_uint32 length) +{ + /* + * 1) + */ + + module_forward_flush(); + + /* + * 2) + */ + + platform_serial_write(PLATFORM_SERIAL_PRIMARY, + (t_uint8*)message, + length); + + MODULE_LEAVE(); +} + +/* + * this function flushes the pending text by sending the buffered text before + * reinitializing the buffer. + * + * steps: + * + * 1) if there is nothing in the buffer, leave. + * 2) send the buffered text. note that the size if first set to zero + * in order to avoid infinite loops since the send() function will + * call the flush() function + * 3) reset the buffer. + */ + +t_error module_forward_flush(void) +{ + t_uint32 size; + + /* + * 1) + */ + + size = _module_forward.size; + + if (_module_forward.size == 0) + MODULE_LEAVE(); + + /* + * 2) + */ + + _module_forward.size = 0; + + if (module_forward_send(_module_forward.buffer, + size) != ERROR_OK) + MODULE_ESCAPE("unable to send the buffer to the serial line"); + + /* + * 3) + */ + + memset(_module_forward.buffer, + 0x0, + sizeof (_module_forward.buffer)); + + MODULE_LEAVE(); +} + +/* + * this function adds a single character to the buffer. + * + * steps: + * + * 1) add the character to the buffer. + * 2) whenever the buffer is full or the character '\n' is received, the + * buffer is flushed into a text message. + */ + +void module_forward_character(char c) +{ + /* + * 1) + */ + + _module_forward.buffer[_module_forward.size++] = c; + + /* + * 2) + */ + + if ((_module_forward.size >= (sizeof (_module_forward.buffer) - 1)) || + (c == '\n')) + module_forward_flush(); +} + +/* + * this function loads the module. + * + * steps: + * + * 1) display a message. + * 2) initialize the manager's structure. + * 3) initialize the platform serial line. + * 4) initialize the console module in order to receive everything. + */ + +t_error module_forward_load(void) +{ + /* + * 1) + */ + + module_call(console, message, + '+', "loading the 'forward' module\n"); + + /* + * 2) + */ + + memset(&_module_forward, 0x0, sizeof (mm_forward)); + + /* + * 3) + */ + + platform_serial_setup(PLATFORM_SERIAL_PRIMARY, + PLATFORM_SERIAL_BR57600, + PLATFORM_SERIAL_8N1); + + /* + * 4) + */ + + module_call(console, set, + module_forward_character, + NULL); + + MODULE_LEAVE(); +} + +/* + * this function unloads the module. + */ + +t_error module_forward_unload(void) +{ + module_call(console, message, + '+', "unloading the 'forward' module\n"); + + MODULE_LEAVE(); +} diff --git a/kaneton/modules/forward/include/forward.h b/kaneton/modules/forward/include/forward.h new file mode 100644 index 0000000..b50abf5 --- /dev/null +++ b/kaneton/modules/forward/include/forward.h @@ -0,0 +1,57 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kane...aneton/modules/forward/include/forward.h + * + * created julien quintard [wed jun 6 16:25:44 2007] + * updated julien quintard [fri dec 10 14:12:31 2010] + */ + +#ifndef MODULES_FORWARD_FORWARD_H +#define MODULES_FORWARD_FORWARD_H 1 + +/* + * ---------- structures ------------------------------------------------------ + */ + +/* + * the module manager. + */ + +typedef struct +{ + char buffer[128]; + t_uint32 size; +} mm_forward; + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../forward.c + */ + +/* + * ../forward.c + */ + +t_error module_forward_send(char* message, + t_uint32 length); + +t_error module_forward_flush(void); + +void module_forward_character(char c); + +t_error module_forward_load(void); + +t_error module_forward_unload(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/modules/modules.c b/kaneton/modules/modules.c new file mode 100644 index 0000000..37430e6 --- /dev/null +++ b/kaneton/modules/modules.c @@ -0,0 +1,12 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/modules/modules.c + * + * created julien quintard [fri may 1 21:02:41 2009] + * updated julien quintard [fri may 1 21:02:44 2009] + */ diff --git a/kaneton/modules/modules.h b/kaneton/modules/modules.h new file mode 100644 index 0000000..4b08447 --- /dev/null +++ b/kaneton/modules/modules.h @@ -0,0 +1,115 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton.STABLE/kaneton/modules/modules.h + * + * created julien quintard [fri may 1 12:58:24 2009] + * updated julien quintard [sun dec 5 00:08:39 2010] + */ + +#ifndef MODULES_MODULES_H +#define MODULES_MODULES_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include +#include +#include +#include + +/* + * ---------- macro-functions ------------------------------------------------- + */ + +#define module_load(_module_) \ + module_call_ ## _module_ (load) + +#define module_unload(_module_) \ + module_call_ ## _module_ (unload) + +#define module_call(_module_, _function_, _arguments_...) \ + module_call_ ## _module_ (_function_, ##_arguments_) + +/* + * ---------- modules --------------------------------------------------------- + */ + +/* + * console + */ +#ifdef MODULE_console +# define module_call_console(_function_, _arguments_...) \ + module_console_ ## _function_ (_arguments_) +#else +# define module_call_console(_function_, _arguments_...) +#endif + +/* + * report + */ +#ifdef MODULE_report +# define module_call_report(_function_, _arguments_...) \ + module_report_ ## _function_ (_arguments_) +#else +# define module_call_report(_function_, _arguments_...) +#endif + +/* + * forward + */ +#ifdef MODULE_forward +# define module_call_forward(_function_, _arguments_...) \ + module_forward_ ## _function_ (_arguments_) +#else +# define module_call_forward(_function_, _arguments_...) +#endif + +/* + * test + */ +#ifdef MODULE_test +# define module_call_test(_function_, _arguments_...) \ + module_test_ ## _function_ (_arguments_) +#else +# define module_call_test(_function_, _arguments_...) +#endif + +/* + * ---------- macro functions ------------------------------------------------- + */ + +/* + * escape + */ + +#define MODULE_ESCAPE(_format_, _arguments_...) \ + { \ + module_call(report, record, \ + _format_ " (%s:%u)", \ + ##_arguments_, __FUNCTION__, __LINE__); \ + \ + return (ERROR_KO); \ + } + +/* + * leave + */ + +#define MODULE_LEAVE() \ + { \ + return (ERROR_OK); \ + } + +#endif diff --git a/kaneton/modules/report/Makefile b/kaneton/modules/report/Makefile new file mode 100644 index 0000000..5b3778e --- /dev/null +++ b/kaneton/modules/report/Makefile @@ -0,0 +1,79 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton.NEW/kaneton/modules/report/Makefile +# +# created julien quintard [wed apr 15 07:00:50 2009] +# updated julien quintard [tue nov 23 10:34:52 2010] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/core/report + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +REPORT_LO := report.lo + +REPORT_C := report.c + +REPORT_O := $(REPORT_C:.c=.o) + +REPORT_INCLUDE := $(directory)/include/report.h + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(REPORT_LO) + +$(REPORT_LO): $(REPORT_O) + $(call env_remove,$(REPORT_LO),) + + $(call env_archive,$(REPORT_LO),$(REPORT_O),) + +clear: + $(call env_remove,$(REPORT_O),) + + $(call env_remove,$(REPORT_LO),) + + $(call env_purge,) + +prototypes: + + $(call env_prototypes,$(REPORT_INCLUDE),) + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(REPORT_C),) + +dependencies: + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/modules/report/include/report.h b/kaneton/modules/report/include/report.h new file mode 100644 index 0000000..993a642 --- /dev/null +++ b/kaneton/modules/report/include/report.h @@ -0,0 +1,65 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/modules/report/include/report.h + * + * created julien quintard [wed jun 6 16:25:44 2007] + * updated julien quintard [fri dec 10 14:12:51 2010] + */ + +#ifndef MODULES_REPORT_REPORT_H +#define MODULES_REPORT_REPORT_H 1 + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#include + +/* + * ---------- structures ------------------------------------------------------ + */ + +/* + * the module manager. + */ + +typedef struct +{ + char buffer[4096]; + t_uint32 offset; +} mm_report; + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../report.c + */ + +/* + * ../report.c + */ + +void module_report_dump(void); + +void module_report_character(char c); + +void module_report_attribute(t_uint8 attribute); + +void module_report_record(char* fmt, + ...); + +t_error module_report_load(void); + +t_error module_report_unload(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/modules/report/report.c b/kaneton/modules/report/report.c new file mode 100644 index 0000000..7fdef3e --- /dev/null +++ b/kaneton/modules/report/report.c @@ -0,0 +1,171 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/modules/report/report.c + * + * created matthieu bucchianeri [sat jun 16 18:10:38 2007] + * updated julien quintard [thu apr 7 15:13:06 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this module provides a functionality for reporting and compiling errors + * in order to display a hiearchical report should an error occur. + */ + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#if !defined(MODULE_console) +# error "the 'report' module depends upon the 'console' module" +#endif + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the module manager. + */ + +mm_report _module_report; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function dumps the report. + */ + +void module_report_dump(void) +{ + t_sint32 i; + t_sint32 j; + + if (_module_report.offset == 0) + return; + + module_call(console, message, + '!', "report:\n"); + + for (i = _module_report.offset - 1, j = i; i >= 0; i--) + { + if (_module_report.buffer[i] == '\0' && (j - i) > 0) + { + module_call(console, message, + '!', " %s\n", + _module_report.buffer + i + 1); + + j = i; + } + } + + if ((j - i) > 0) + module_call(console, message, + '!', " %s\n", + _module_report.buffer + i + 1); + + _module_report.offset = 0; +} + +/* + * this function is called by format() and records a single character + * in the static report buffer. + */ + +void module_report_character(char c) +{ + _module_report.buffer[_module_report.offset++] = c; +} + +/* + * this function is called by format() to specify the attribute. + */ + +void module_report_attribute(t_uint8 attribute) +{ +} + +/* + * this function records an error message. + * + * steps: + * + * 1) call format() so that the the arguments can be treated. + * 2) mark the end of the recorded message. + */ + +void module_report_record(char* fmt, + ...) +{ + va_list args; + + /* + * 1) + */ + + va_start(args, fmt); + + format(module_report_character, module_report_attribute, + fmt, args); + + va_end(args); + + /* + * 2) + */ + + _module_report.buffer[_module_report.offset++] = '\0'; +} + +/* + * this function loads the report module. + * + * steps: + * + * 1) display a message. + * 2) initialize the report structure. + */ + +t_error module_report_load(void) +{ + /* + * 1) + */ + + module_call(console, message, + '+', "loading the 'report' module\n"); + + /* + * 2) + */ + + memset(&_module_report, 0x0, sizeof (mm_report)); + + MODULE_LEAVE(); +} + +/* + * this function unloads the module. + */ + +t_error module_report_unload(void) +{ + module_call(console, message, + '+', "unloading the 'report' module\n"); + + MODULE_LEAVE(); +} diff --git a/kaneton/modules/test/Makefile b/kaneton/modules/test/Makefile new file mode 100644 index 0000000..67a8caa --- /dev/null +++ b/kaneton/modules/test/Makefile @@ -0,0 +1,79 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton.STABLE/kaneton/modules/test/Makefile +# +# created julien quintard [wed apr 15 07:00:50 2009] +# updated julien quintard [sun dec 5 16:58:08 2010] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := kaneton/core/test + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers dependencies + +# +# ---------- variables -------------------------------------------------------- +# + +TEST_LO := test.lo + +TEST_C := test.c + +TEST_O := $(TEST_C:.c=.o) + +TEST_INCLUDE := $(directory)/include/test.h + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(TEST_LO) + +$(TEST_LO): $(TEST_O) + $(call env_remove,$(TEST_LO),) + + $(call env_archive,$(TEST_LO),$(TEST_O),) + +clear: + $(call env_remove,$(TEST_O),) + + $(call env_remove,$(TEST_LO),) + + $(call env_purge,) + +prototypes: + $(call env_prototypes,$(TEST_INCLUDE),) + +headers: + $(call env_remove,$(_DEPENDENCY_MK_),) + + $(call env_headers,$(TEST_C),) + +dependencies: + + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/kaneton/modules/test/include/test.h b/kaneton/modules/test/include/test.h new file mode 100644 index 0000000..2fc2eda --- /dev/null +++ b/kaneton/modules/test/include/test.h @@ -0,0 +1,121 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/modules/test/include/test.h + * + * created julien quintard [wed jun 6 16:25:44 2007] + * updated julien quintard [fri dec 10 14:13:18 2010] + */ + +#ifndef MODULES_TEST_TEST_H +#define MODULES_TEST_TEST_H 1 + +/* + * ---------- types ----------------------------------------------------------- + */ + +/* + * this type represents a test function pointer. + */ + +typedef void (*mf_test)(void); + +/* + * the tests inventory. + * + * note that the inventory array 'tests' is automatically generated. + */ + +typedef struct +{ + char* symbol; + mf_test function; +} ms_test_function; + +/* + * a test command associated with its function pointer. + */ + +typedef struct +{ + char* command; + t_error (*function)(char*); +} ms_test_command; + +/* + * the module manager. + */ + +typedef struct +{ + char buffer[128]; + t_uint32 size; +} mm_test; + +/* + * ---------- macros ---------------------------------------------------------- + */ + +/* + * types + */ + +#define MODULE_TEST_TYPE_COMMAND 0x1 +#define MODULE_TEST_TYPE_TEXT 0x2 + +/* + * magic + */ + +#define MODULE_TEST_MAGIC 0xf4859632 + +/* + * ---------- prototypes ------------------------------------------------------ + * + * ../test.c + */ + +/* + * ../test.c + */ + +t_uint32 module_test_checksum(void* data, + t_uint32 size, + t_uint32* crc); + +t_error module_test_send(t_uint8 type, + char* message, + t_uint32 length); + +t_error module_test_receive(t_uint8* type, + char* message); + +t_error module_test_issue(char* command); + +t_error module_test_flush(void); + +void module_test_character(char c); + +t_error module_test_locate(char* symbol, + mf_test* function); + +t_error module_test_call(char* symbol); + +void module_test_dump(void); + +t_error module_test_run(void); + +t_error module_test_load(void); + +t_error module_test_unload(void); + + +/* + * eop + */ + +#endif diff --git a/kaneton/modules/test/test.c b/kaneton/modules/test/test.c new file mode 100644 index 0000000..b2449bf --- /dev/null +++ b/kaneton/modules/test/test.c @@ -0,0 +1,531 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton/kaneton/modules/test/test.c + * + * created matthieu bucchianeri [sat jun 16 18:10:38 2007] + * updated julien quintard [sun may 8 12:21:12 2011] + */ + +/* + * ---------- information ----------------------------------------------------- + * + * this file implements the in-kernel test system that basically waits for + * incoming calls and launches the according tests. + * + * this module is partially portable as it relies on the platform for serial + * communication. + */ + +/* + * ---------- dependencies ---------------------------------------------------- + */ + +#if !defined(MODULE_console) +# error "the 'test' module depends upon the 'console' module" +#endif + +#if defined(MODULE_forward) +# error "the 'forward' module cannot be activated with the 'test' module" +#endif + +/* + * ---------- includes -------------------------------------------------------- + */ + +#include + +/* + * ---------- extern ---------------------------------------------------------- + */ + +/* + * this array of mappings between function names and function symbols is + * automatically generated. + */ + +extern ms_test_function _module_test_functions[]; + +/* + * ---------- globals --------------------------------------------------------- + */ + +/* + * the module manager. + */ + +mm_test _module_test; + +/* + * ---------- functions ------------------------------------------------------- + */ + +/* + * this function performs a very simple XOR-based checksum on the given data. + */ + +t_uint32 module_test_checksum(void* data, + t_uint32 size, + t_uint32* crc) +{ + t_uint8* d = data; + t_uint32 i; + + for (*crc = 0, i = 0; i < size; i++) + *crc ^= d[i]; + + MODULE_LEAVE(); +} + +/* + * this function sends a message, either a text or command. + * + * steps: + * + * 1) flushes the buffer to make sure there is no pending text messages. + * 2) construct a message by computing the CRC and setting the magic. + * 3) send the message by issuing multiple serial_write() + */ + +t_error module_test_send(t_uint8 type, + char* message, + t_uint32 length) +{ + t_uint32 magic; + t_uint32 crc; + + /* + * 1) + */ + + module_test_flush(); + + /* + * 2) + */ + + magic = MODULE_TEST_MAGIC; + + if (module_test_checksum(message, length, &crc) != ERROR_OK) + MACHINE_ESCAPE("unable to compute the message's CRC"); + + /* + * 3) + */ + + platform_serial_write(PLATFORM_SERIAL_PRIMARY, + (t_uint8*)&magic, + sizeof (t_uint32)); + + platform_serial_write(PLATFORM_SERIAL_PRIMARY, + (t_uint8*)&type, + sizeof (t_uint8)); + + platform_serial_write(PLATFORM_SERIAL_PRIMARY, + (t_uint8*)&length, + sizeof (t_uint32)); + + platform_serial_write(PLATFORM_SERIAL_PRIMARY, + (t_uint8*)&crc, + sizeof (t_uint32)); + + platform_serial_write(PLATFORM_SERIAL_PRIMARY, + (t_uint8*)message, + length); + + MODULE_LEAVE(); +} + +/* + * this function receives a message, either a text or command. + * + * steps: + * + * 1) receive and check the magic number. + * 2) receive the content. + * 3) check the checksum. + */ + +t_error module_test_receive(t_uint8* type, + char* message) +{ + t_uint32 magic; + t_uint32 length; + t_uint32 crc; + t_uint32 c; + + /* + * 1) + */ + + platform_serial_read(PLATFORM_SERIAL_PRIMARY, + (t_uint8*)&magic, + sizeof (t_uint32)); + + if (magic != MODULE_TEST_MAGIC) + MODULE_ESCAPE("invalid magic number received"); + + /* + * 2) + */ + + platform_serial_read(PLATFORM_SERIAL_PRIMARY, + (t_uint8*)type, + sizeof (t_uint8)); + + platform_serial_read(PLATFORM_SERIAL_PRIMARY, + (t_uint8*)&length, + sizeof (t_uint32)); + + platform_serial_read(PLATFORM_SERIAL_PRIMARY, + (t_uint8*)&crc, + sizeof (t_uint32)); + + platform_serial_read(PLATFORM_SERIAL_PRIMARY, + (t_uint8*)message, + length); + + /* + * 3) + */ + + if (module_test_checksum(message, length, &c) != ERROR_OK) + MODULE_ESCAPE("unable to compute the message's CRC"); + + if (crc != c) + MODULE_ESCAPE("invalid received message's CRC"); + + MODULE_LEAVE(); +} + +/* + * this function is a wrapper for sending commands. + */ + +t_error module_test_issue(char* command) +{ + if (module_test_send(MODULE_TEST_TYPE_COMMAND, + command, + strlen(command)) != ERROR_OK) + MODULE_ESCAPE("unable to send a command back to the client"); + + MODULE_LEAVE(); +} + +/* + * this function flushes the pending text by sending a text message + * before reinitializing the buffer. + * + * steps: + * + * 1) if there is nothing in the buffer, just leave. + * 2) send the buffered text to the serial line. note that the buffer size + * is set to zero to avoid infinite loops since send() will call flush(). + * 3) re-initialize the buffer. + */ + +t_error module_test_flush(void) +{ + t_uint32 size; + + /* + * 1) + */ + + size = _module_test.size; + + if (_module_test.size == 0) + MODULE_LEAVE(); + + /* + * 2) + */ + + _module_test.size = 0; + + if (module_test_send(MODULE_TEST_TYPE_TEXT, + _module_test.buffer, + size) != ERROR_OK) + MODULE_ESCAPE("unable to send the buffer back to the client"); + + /* + * 3) + */ + + memset(_module_test.buffer, + 0x0, + sizeof (_module_test.buffer)); + + MODULE_LEAVE(); +} + +/* + * this function adds a single character to the buffer. this function + * is called by format(). + * + * then, whenever the buffer is full or the character '\n' is received, + * the buffer is flushed into a text message. + * + * steps: + * + * 1) record the character. + * 2) flush if necessary. + */ + +void module_test_character(char c) +{ + /* + * 1) + */ + + _module_test.buffer[_module_test.size++] = c; + + /* + * 2) + */ + + if ((_module_test.size >= (sizeof (_module_test.buffer) - 1)) || + (c == '\n')) + module_test_flush(); +} + +/* + * this function locates a test function according to its symbol name. + */ + +t_error module_test_locate(char* symbol, + mf_test* function) +{ + unsigned int i; + + for (i = 0; _module_test_functions[i].symbol != NULL; i++) + if (strcmp(_module_test_functions[i].symbol, symbol) == 0) + { + *function = _module_test_functions[i].function; + + MODULE_LEAVE(); + } + + MODULE_ESCAPE("unable to locate the test symbol"); +} + +/* + * this function takes a test function symbol and calls it. + * + * steps: + * + * 1) locate the symbol in the table. + * 2) issue the enter command, specifying the beginning of the test. + * 3) call the test function. + * 4) issue the leave command. + */ + +t_error module_test_call(char* symbol) +{ + mf_test function; + + /* + * 1) + */ + + if (module_test_locate(symbol, &function) != ERROR_OK) + MODULE_ESCAPE("invalid test symbol"); + + /* + * 2) + */ + + module_test_issue("[enter]"); + + /* + * 3) + */ + + function(); + + /* + * 4) + */ + + module_test_issue("[leave]"); + + MODULE_LEAVE(); +} + +/* + * this function dumps the registered test functions. + */ + +void module_test_dump(void) +{ + unsigned int i; + + module_call(console, message, + '#', "[tests]\n"); + + for (i = 0; _module_test_functions[i].symbol != NULL; i++) + module_call(console, message, + '#', " [%s] 0x%x\n", + _module_test_functions[i].symbol, + _module_test_functions[i].function); +} + +/* + * this function actually runs the test system by first initializing + * the serial line, performing a handshake and waiting for commands. + * + * steps: + * + * 1) display a message. + * 2) set up the serial port. + * 3) tell the console module to forward everything to the test module. + * 4) issues the ready command which the client must be waiting for. + * 5) wait for commands and handle them. + */ + +t_error module_test_run(void) +{ + ms_test_command commands[] = + { + { "[call] ", module_test_call }, + { NULL, NULL } + }; + + /* + * 1) + */ + + module_call(console, message, + '+', "running the 'test' module\n"); + + /* + * 2) + */ + + platform_serial_initialize(); + + platform_serial_setup(PLATFORM_SERIAL_PRIMARY, + PLATFORM_SERIAL_BR57600, + PLATFORM_SERIAL_8N1); + + /* + * 3) + */ + + module_call(console, set, + module_test_character, + NULL); + + /* + * 4) + */ + + module_test_issue("[ready]"); + + /* + * 5) + */ + + while (1) + { + char message[512]; + t_uint8 type; + unsigned int i; + + memset(message, 0x0, sizeof (message)); + + if (module_test_receive(&type, message) != ERROR_OK) + MODULE_ESCAPE("unable to received a test request"); + + if (type != MODULE_TEST_TYPE_COMMAND) + MODULE_ESCAPE("invalid command type"); + + for (i = 0; commands[i].command != NULL; i++) + { + if (strncmp(commands[i].command, + message, + strlen(commands[i].command)) == 0) + { + t_uint32 offset = strlen(commands[i].command); + + if (commands[i].function(message + offset) != ERROR_OK) + MODULE_ESCAPE("an error occured in the triggered " + "command"); + + break; + } + } + + if (commands[i].command == NULL) + MODULE_ESCAPE("unknown command name"); + } + + MODULE_LEAVE(); +} + +/* + * this function loads the module. + * + * steps: + * + * 1) display a message. + * 2) initialize the manager's structure. + * 3) initialize the serial manager. + */ + +t_error module_test_load(void) +{ + /* + * 1) + */ + + module_call(console, message, + '+', "loading the 'test' module\n"); + + /* + * 2) + */ + + memset(&_module_test, 0x0, sizeof (mm_test)); + + /* + * 3) + */ + + if (platform_serial_initialize() != ERROR_OK) + MODULE_ESCAPE("unable to initialize the serial manager"); + + MODULE_LEAVE(); +} + +/* + * this function unloads the module. + * + * steps: + * + * 1) display a message. + * 2) clean the serial manager. + */ + +t_error module_test_unload(void) +{ + /* + * 1) + */ + + module_call(console, message, + '+', "unloading the 'test' module\n"); + + /* + * 2) + */ + + if (platform_serial_clean() != ERROR_OK) + MODULE_ESCAPE("unable to clean the serial manager"); + + MODULE_LEAVE(); +} diff --git a/license/Makefile b/license/Makefile new file mode 100644 index 0000000..e6272ff --- /dev/null +++ b/license/Makefile @@ -0,0 +1,37 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/history/Makefile +# +# created julien quintard [sun jun 10 14:53:00 2007] +# updated julien quintard [wed apr 22 12:21:12 2009] +# + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers + +# +# ---------- rules ------------------------------------------------------------ +# + +main: + +clear: + $(call env_purge) + +prototypes: + +headers: diff --git a/license/kaneton.txt b/license/kaneton.txt new file mode 100644 index 0000000..b3980e4 --- /dev/null +++ b/license/kaneton.txt @@ -0,0 +1,10 @@ +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +--------------------------------------------------------------- + +0. This licence is nothing more than a link to the pedagogical licence. + +1. Any program under the kaneton licence is in fact under the terms and + conditions of the pedagogical licence. + +-- +END OF TERMS AND CONDITIONS diff --git a/license/pedagogical.txt b/license/pedagogical.txt new file mode 100644 index 0000000..8230ef6 --- /dev/null +++ b/license/pedagogical.txt @@ -0,0 +1,159 @@ +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +--------------------------------------------------------------- + +0. This License applies to any program or other work which contains a + notice placed by the copyright holder saying it may be distributed + under the terms of this License. The "Program", below, refers + to any such program or work, and a "work based on the Program" + means either the Program or any derivative work under copyright law: + that is to say, a work containing the Program or a portion of it, + either verbatim or with modifications and/or translated into another + language. (Hereinafter, translation is included without limitation in + the term "modification".) Each licensee is addressed as "you". + + +1. You must not copy or distribute copies of the Program's source + code, object code or executable form without explicit authorization + from the maintainers. + If you have this authorization, you must conspicuously and + appropriately publish on each copy an appropriate copyright notice + and disclaimer of warranty; keep intact all the notices that refer + to this License and to the absence of any warranty; and give any + other recipients of the Program a copy of this License along with + the Program. + + +2. You may modify your copy or copies of the Program or any portion of + it, thus forming a work based on the Program, provided that you + meet all of these conditions : + + a. You must not publish your work without explicit authorization + from the maintainers. + + b. You must send to the maintainers any work that in whole or in + part contains or is derived from the Program or any part thereof. + + c. You must cause any work that you send, that in whole or in + part contains or is derived from the Program or any part thereof, + to be licensed as a whole under the terms of this License. + + d. You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + e. If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you + provide a warranty) and telling the user how to view a copy + of this License. (Exception: if the Program itself is interactive + but does not normally print such an announcement, your work based + on the Program is not required to print an announcement.) + + +3. Access to the Program's source is granted if either: + + a. You want to make the Program evolve + + b. You have pedagogical goals + + + These requirements apply to the modified work as a whole. If + identifiable sections of that work are not derived from the Program, + and can be reasonably considered independent and separate works in + themselves, then this License, and its terms, do not apply to those + sections when you distribute them as separate works. But when you + distribute the same sections as part of a whole which is a work based + on the Program, the distribution of the whole must be on the terms of + this License, whose permissions for other licensees extend to the + entire whole, and thus to each and every part regardless of who wrote + it. + + Thus, it is not the intent of this section to claim rights or contest + your rights to work written entirely by you; rather, the intent is to + exercise the right to control the distribution of derivative or + collective works based on the Program. + + In addition, mere aggregation of another work not based on the Program + with the Program (or with a work based on the Program) on a volume of + a storage or distribution medium does not bring the other work under + the scope of this License. + + +4. You may not copy, modify, sublicense, or distribute the Program + except as expressly provided under this License. Any attempt + otherwise to copy, modify, sublicense or distribute the Program is + void, and will automatically terminate your rights under this + License. However, parties who have received copies, or rights, + from you under this License will not have their licenses + terminated so long as such parties remain in full compliance. + + +5. You are not required to accept this License, since you have not + signed it. However, nothing else grants you permission to modify + or distribute the Program or its derivative works. These actions + are prohibited by law if you do not accept this + License. Therefore, by modifying or distributing the Program (or + any work based on the Program), you indicate your acceptance of + this License to do so, and all its terms and conditions for + copying, distributing or modifying the Program or works based on + it. + + +6. Each time you redistribute the Program (or any work based on the + Program), the recipient automatically receives a license from the + original licensor to copy, distribute or modify the Program subject + to these terms and conditions. You may not impose any further + restrictions on the recipients' exercise of the rights granted + herein. You are not responsible for enforcing compliance by third + parties to this License. + + +8. If the distribution and/or use of the Program is restricted in + certain countries either by patents or by copyrighted interfaces, + the original copyright holder who places the Program under this + License may add an explicit geographical distribution limitation + excluding those countries, so that distribution is permitted only + in or among countries not thus excluded. In such case, this License + incorporates the limitation as if written in the body of this + License. + + +9. We may publish revised and/or new versions of this License from + time to time. It may evolve considering new contributors + needs. Contact us if you have any request. Such new versions will + be similar in spirit to the present version, but may differ in + detail to address new problems or concerns. + + +10. If you wish to incorporate parts of the Program into other free + programs whose distribution conditions are different, write to the + author to ask for permission, we sometimes make exceptions for this. + + +NO WARRANTY + +11. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY + APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE + COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE + RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH + YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF + ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN + WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY + MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE + LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, + INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR + INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF + DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU + OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY + OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +-- +END OF TERMS AND CONDITIONS diff --git a/sample/Makefile b/sample/Makefile new file mode 100644 index 0000000..af793b6 --- /dev/null +++ b/sample/Makefile @@ -0,0 +1,47 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/sample/Makefile +# +# created julien quintard [sun jun 10 14:54:43 2007] +# updated julien quintard [fri may 1 21:55:31 2009] +# + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers + +# +# ---------- variables -------------------------------------------------------- +# + +SUBDIRS := chiche + +# +# ---------- rules ------------------------------------------------------------ +# + +main: + +clear: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,clear,) ; \ + done + + $(call env_purge,) + +prototypes: + +headers: diff --git a/sample/chiche/Makefile b/sample/chiche/Makefile new file mode 100644 index 0000000..bc4787b --- /dev/null +++ b/sample/chiche/Makefile @@ -0,0 +1,76 @@ +## +## licence kaneton licence +## +## project kaneton +## +## file /home/buckman/kaneton/drivers/mod/Makefile +## +## created julien quintard [fri feb 11 03:00:09 2005] +## updated matthieu bucchianeri [sat may 5 18:22:24 2007] +## + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: libs clear prototypes + +# +# ---------- variables -------------------------------------------------------- +# + +CHICHE := chiche + +CHICHE_C := chiche.c +CHICHE_O := $(CHICHE_C:.c=.o) +CHICHE_H := $(_SAMPLE_DIR_)/chiche/chiche.h + +CRT_S := _crt.S +CRT_O := $(CRT_S:.S=.o) + +KAYOU_C := _kayou.c +KAYOU_O := $(KAYOU_C:.c=.o) + +# +# ---------- rules ------------------------------------------------------------ +# +ifeq ($(behaviour),default) + +main: dependencies $(CHICHE) + +$(CHICHE): $(CRT_O) $(KAYOU_O) $(CHICHE_O) + $(call env_executable,$(CHICHE), \ + $(CRT_O) \ + $(KAYOU_O) \ + $(CHICHE_O), \ + $(_SERVICE_LAYOUT_), \ + $(ENV_OPTION_NO_STANDARD)) + +clear: + $(call env_remove,$(CRT_O),) + $(call env_remove,$(KAYOU_O),) + $(call env_remove,$(CHICHE_O),) + $(call env_remove,$(CHICHE),) + + $(call env_purge,) + +prototypes: + echo $(CHICHE_H) + $(call env_prototypes,$(CHICHE_H),) + +headers: + +dependencies: + +endif +# +# ---------- dependencies ----------------------------------------------------- +# + +-include ./$(_DEPENDENCY_MK_) diff --git a/sample/chiche/_crt.S b/sample/chiche/_crt.S new file mode 100644 index 0000000..491c028 --- /dev/null +++ b/sample/chiche/_crt.S @@ -0,0 +1,70 @@ +.text +.global _start +.type _start,%function + +.type main,%function +.type _kayou,%function + +_start: + /* Clear the frame pointer. The ABI suggests this be done, to mark + the outermost frame obviously. */ + xorl %ebp, %ebp + + /* Extract the arguments as encoded on the stack and set up + the arguments for `main': argc, argv. envp will be determined + later in __libc_start_main. */ + + movl $__task_id, %edi + popl 0(%edi) + popl 4(%edi) + movl $__as_id, %edi + popl 0(%edi) + popl 4(%edi) + + popl %esi /* Pop the argument count. */ + movl %esp, %ecx /* argv starts just at the current stack top.*/ + + /* Before pushing the arguments align the stack to a 16-byte + (SSE needs 16-byte alignment) boundary to avoid penalties from + misaligned accesses. Thanks to Edward Seidl + for pointing this out. */ + andl $0xfffffff0, %esp + pushl %eax /* Push garbage because we allocate + 28 more bytes. */ + + /* Provide the highest stack address to the user code (for stacks + which grow downwards). */ + pushl %esp + + pushl %ecx /* Push second argument: argv. */ + pushl %esi /* Push first argument: argc. */ + + pushl $main + + /* Call the user's main function, and exit with its value. + But let the libc call main. */ + call _kayou + + hlt /* Crash if somehow `exit' does return. */ +.size _start,.-_start + +/* Define a symbol for the first piece of initialized data. */ +.data +.global __task_id +__task_id: +.long 0 +.long 0 +.global __as_id +__as_id: +.long 0 +.long 0 +.global __system_id +__system_id: +.long 0 +.long 0 + +.global __data_start +__data_start: +.long 0 +.weak data_start + data_start = __data_start diff --git a/sample/chiche/_kayou.c b/sample/chiche/_kayou.c new file mode 100644 index 0000000..eff0180 --- /dev/null +++ b/sample/chiche/_kayou.c @@ -0,0 +1,38 @@ +void *__libc_stack_end = (void*)0; + +/* + * Declare the __environ global variable and create a strong alias environ. + * Note: Apparently we must initialize __environ to ensure that the strong + * environ symbol is also included. + */ +char **__environ = 0; + +/* __uClibc_main is the new main stub for uClibc. This function is + * called from crt1 (version 0.9.28 or newer), after ALL shared libraries + * are initialized, just before we call the application's main function. + */ +void _kayou(int (*main)(int, char **, char **), + int argc, + char **argv, + void *stack_end) +{ + __libc_stack_end = stack_end; + + /* The environment begins right after argv. */ + __environ = &argv[argc + 1]; + + /* If the first thing after argv is the arguments + * the the environment is empty. */ + if ((char *) __environ == *argv) { + /* Make __environ point to the NULL at argv[argc] */ + __environ = &argv[argc]; + } + + /* + * Finally, invoke application's main and then exit. + */ + main(argc, argv, __environ); + + while (1) + ; +} diff --git a/sample/chiche/chiche.c b/sample/chiche/chiche.c new file mode 100644 index 0000000..24bef32 --- /dev/null +++ b/sample/chiche/chiche.c @@ -0,0 +1,26 @@ +/* + * ---------- header ---------------------------------------------------------- + * + * project kaneton + * + * license kaneton + * + * file /home/mycure/kaneton.STABLE/sample/chiche/chiche.c + * + * created matthieu bucchianeri [sat jun 9 18:36:19 2007] + * updated julien quintard [sat dec 4 01:07:04 2010] + */ + +#include "chiche.h" + +/* + * main() function. + */ + +int main(void) +{ + while (1) + ; + + return (0); +} diff --git a/sample/chiche/chiche.h b/sample/chiche/chiche.h new file mode 100644 index 0000000..f6a4578 --- /dev/null +++ b/sample/chiche/chiche.h @@ -0,0 +1,32 @@ +/* + * licence kaneton licence + * + * project kaneton + * + * file /home/buckman/kaneton.bak/drivers/cons-simple/cons-simple.h + * + * created matthieu bucchianeri [thu may 31 21:28:04 2007] + * updated matthieu bucchianeri [sat jun 9 22:37:53 2007] + */ + +#ifndef SAMPLE_CHICHE_CHICHE_H +#define SAMPLE_CHICHE_CHICHE_H 1 + +/* + * ---------- prototypes ------------------------------------------------------ + * + * chiche.c + */ + +/* + * chiche.c + */ + +int main(void); + + +/* + * eop + */ + +#endif diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000..c55e14b --- /dev/null +++ b/test/Makefile @@ -0,0 +1,60 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/export/output/kaneton/test/Makefile +# +# created julien quintard [fri jun 29 11:19:40 2007] +# updated julien quintard [sat feb 5 12:10:10 2011] +# + +# +# ---------- component -------------------------------------------------------- +# + +component := test + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers + +# +# ---------- variables -------------------------------------------------------- +# + +SUBDIRS := client \ + packages + +# +# ---------- rules ------------------------------------------------------------ +# + +main: + +clear: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,clear,) ; \ + done + + $(call env_purge,) + +prototypes: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,clear,) ; \ + done + +headers: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,clear,) ; \ + done diff --git a/test/README b/test/README new file mode 100644 index 0000000..10deb3d --- /dev/null +++ b/test/README @@ -0,0 +1,15 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton.STABLE/test/README +# +# created julien quintard [tue nov 2 07:38:27 2010] +# updated julien quintard [tue nov 2 07:39:23 2010] +# + +This directory contains the whole test system which is composed of a server, +a client, some scripts as well as test suites composed of several tests. diff --git a/test/client/Makefile b/test/client/Makefile new file mode 100644 index 0000000..48baf85 --- /dev/null +++ b/test/client/Makefile @@ -0,0 +1,61 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/test/client/Makefile +# +# created julien quintard [fri jun 29 11:19:40 2007] +# updated julien quintard [thu feb 10 10:35:45 2011] +# + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main build run clear prototypes headers information- \ + information test- test list- list display- display \ + submit submit- + +# +# ---------- rules ------------------------------------------------------------ +# + +main: + $(call env_launch,$(_TEST_CLIENT_SCRIPT_),,) + +information- information: + $(call env_launch,$(_TEST_CLIENT_SCRIPT_),$@,) + +test- test: + $(call env_launch,$(_TEST_CLIENT_SCRIPT_),$@,) + +test-%: + $(call env_launch,$(_TEST_CLIENT_SCRIPT_),$@,) + +retest- retest: + $(call env_launch,$(_TEST_CLIENT_SCRIPT_),$@,) + +retest-%: + $(call env_launch,$(_TEST_CLIENT_SCRIPT_),$@,) + +submit- submit: + $(call env_launch,$(_TEST_CLIENT_SCRIPT_),$@,) + +submit-%: + $(call env_launch,$(_TEST_CLIENT_SCRIPT_),$@,) + +clear: + $(call purge,) + +prototypes: + +headers: diff --git a/test/client/README b/test/client/README new file mode 100644 index 0000000..36590e5 --- /dev/null +++ b/test/client/README @@ -0,0 +1,165 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/test/client/README +# +# created julien quintard [tue nov 2 05:48:57 2010] +# updated julien quintard [thu feb 10 11:17:07 2011] +# + +The client script provides the user the possibility to request actions +from the test server. + + $> cd ~/kaneton/test/client/ + $~/kaneton/test/client> make + [!] usage: client.py [command] + + [!] commands: + [!] retest-[identifier] + [!] information + [!] test-[environment]::[suite] + [!] submit-[stage] + $~/kaneton/test/client> + +# +# ---------- information ------------------------------------------------------ +# + +The 'information' command returns information on the user account including +the number of tests consumed, the number of tests left etc. + +The example below illustrates this command. + + $~/kaneton/test/client> make information + [+] configuration: + [+] server: https://test.opaak.org:8421 + [+] capability: /data/mycure/repositories/kaneton/environment/profile/user/julien.quintard/julien.quintard.cap + [+] platform: ibm-pc + [+] architecture: ia32/educational + + [+] information: + [+] profile: + [+] attributes: + [+] identifier: julien.quintard + [+] type: contributor + [+] members: + [+] name: Julien Quintard + [+] email: julien.quintard@gmail.com + [+] suites: + [+] k3: This test suite contains tests related to the execution. + [+] k2: This test suite focuses on the memory management. + [+] k1: This test suite focuses on the event processing. + [+] bugs: This suite contains the tests which fail on the official kaneton implementation. + [+] kaneton: This test suite triggers all the tests. + [+] stages: + [+] k3: This stage evaluates the kaneton's execution functionalities. + [+] k2: This stage evaluates the kaneton's memory management. + [+] k1: This stage evaluates the kaneton's event procesing capabilities. + [+] k0: This stage focuses on the assembly language and the boot process through BIOS services. + [+] environments: + [+] xen: The 'xen' environment is used to thoroughly test a kaneton implementation in a Xen hardware-assisted virtual machine. + [+] qemu: The 'qemu' environment is used to test a kaneton implementation through the QEMU processor emulator. + [+] database: + [+] quotas: + [+] xen: + [+] k3: -1 + [+] k2: -1 + [+] k1: -1 + [+] bugs: -1 + [+] kaneton: -1 + [+] qemu: + [+] ibm-pc.ia32/educational: + [+] k3: -1 + [+] k2: -1 + [+] k1: -1 + [+] bugs: -1 + [+] kaneton: -1 + [+] reports: + [+] xen: + [+] ibm-pc.ia32/educational: + [+] k3: + [+] k2: + [+] k1: + [+] bugs: + [+] kaneton: + [+] qemu: + [+] ibm-pc.ia32/educational: + [+] k3: + [+] k2: + [+] k1: + [+] bugs: + [+] kaneton: + $~/kaneton/test/client> + +# +# ---------- test ------------------------------------------------------------- +# + +The 'test' command issues a request for a test suite to be run in the given +environment so that to stress the current kaneton implementation. + +The syntax for this command is test-[environment]::[suite] where [suite] +can be any of the 'k1', 'k2', 'k3' etc. stages while [environment] can +be 'xen' or 'qemu'. For more information regarding the available suites, +stages, environments etc., please refer to the 'information' command. + + $~/kaneton/test/client> make test-xen::k1 + [+] configuration: + [+] server: https://test.opaak.org:8421 + [+] capability: /data/mycure/repositories/kaneton/environment/profile/user/julien.quintard/julien.quintard.cap + [+] platform: ibm-pc + [+] architecture: ia32/educational + + [+] generating the kaneton snapshot + [+] loading the kaneton snapshot + [+] requesting the server + [+] the snapshot has been scheduled for testing under the identifier: 20110210:105955 + $~/kaneton/test/client> + +# +# ---------- submit ----------------------------------------------------------- +# + +Finally, the 'submit' command enables students to definitely submit a +kaneton implementation so that their work gets evaluated afterwards according +to a specific stage. + +The syntax for this command is submit-[stage]. + + $~/kaneton/test/client> make submit-k3 + [+] configuration: + [+] server: https://test.opaak.org:8421 + [+] capability: /data/mycure/repositories/kaneton/environment/profile/user/julien.quintard/julien.quintard.cap + [+] platform: ibm-pc + [+] architecture: ia32/educational + + [+] generating the kaneton snapshot + [+] loading the kaneton snapshot + [+] requesting the server + [+] the snapshot has been submitted successfully + $~/kaneton/test/client> + +# +# ---------- retest ----------------------------------------------------------- +# + +The 'retest' command provides the administrator the possibility to re-launch +the test suite according to the given identifier. This command is useful should +an unexpected error occur. + +The syntax for this command is retest-[identifier]. + + $~/kaneton/test/client> make retest-20110210:105955 + [+] configuration: + [+] server: https://test.opaak.org:8421 + [+] capability: /data/mycure/repositories/kaneton/environment/profile/user/julien.quintard/julien.quintard.cap + [+] platform: ibm-pc + [+] architecture: ia32/educational + + [+] requesting the server + [+] the snapshot has been re-tested successfully + $~/kaneton/test/client> diff --git a/test/client/client.py b/test/client/client.py new file mode 100644 index 0000000..7945b4d --- /dev/null +++ b/test/client/client.py @@ -0,0 +1,371 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/test/client/client.py +# +# created julien quintard [mon mar 23 00:09:51 2009] +# updated julien quintard [mon jun 13 23:39:28 2011] +# + +# +# ---------- information ------------------------------------------------------ +# +# this script connects to the test server and triggers operations. +# +# note that this script depends on the ktp package which relies on +# the pyopenssl, yaml and other packages. +# + +# +# ---------- packages --------------------------------------------------------- +# + +import sys + +import env +import ktp + +# +# ---------- globals ---------------------------------------------------------- +# + +g_server = None +g_capability = None +g_platform = None +g_architecture = None + +# +# ---------- functions -------------------------------------------------------- +# + +# +# this function prints the usage. +# +def Usage(): + command = None + + env.display(env.HEADER_ERROR, "usage: client.py [command]", + env.OPTION_NONE) + + env.display(env.HEADER_NONE, "", env.OPTION_NONE) + + env.display(env.HEADER_ERROR, "commands:", env.OPTION_NONE) + + for command in c_commands: + env.display(env.HEADER_ERROR, " " + command, env.OPTION_NONE) + +# +# this function simply displays the current configuration. +# +def Warning(): + env.display(env.HEADER_NONE, "", env.OPTION_NONE) + env.display(env.HEADER_OK, "configuration:", env.OPTION_NONE) + env.display(env.HEADER_OK, + " server: " + g_server, + env.OPTION_NONE) + env.display(env.HEADER_OK, + " capability: " + g_capability, + env.OPTION_NONE) + env.display(env.HEADER_OK, + " platform: " + str(g_platform), + env.OPTION_NONE) + env.display(env.HEADER_OK, + " architecture: " + str(g_architecture), + env.OPTION_NONE) + env.display(env.HEADER_NONE, "", env.OPTION_NONE) + +# +# this function dumps any Python data structure in a hierarchical way. +# +def Dump(data, margin = "", alignment = 26): + key = None + length = None + + if isinstance(data, dict): + for key in data: + if not isinstance(data[key], dict) and not isinstance(data[key], list): + length = len(margin) + len(str(key)) + 1 + + env.display(env.HEADER_OK, + margin + str(key) + ":" + + (alignment - length) * " " + + str(data[key]), + env.OPTION_NONE) + else: + env.display(env.HEADER_OK, + margin + str(key) + ":", + env.OPTION_NONE) + + Dump(data[key], margin + " ", alignment) + elif isinstance(data, list): + for element in data: + Dump(element, margin, alignment) + else: + env.display(env.HEADER_OK, + alignment * " " + + str(data), + env.OPTION_NONE) + +# +# this function asks the server for information related to +# the given capability hence user. +# +# these information are then displayed. +# +def Information(server, capability, arguments): + information = None + item = None + + # check the arguments + if len(arguments) != 0: + Usage() + sys.exit(42) + + # warning + Warning() + + # retrieve the information. + information = ktp.xmlrpc.Call(server.Information(capability)) + + # display a message. + env.display(env.HEADER_OK, + "information:", + env.OPTION_NONE) + + # dump the information dictionary. + Dump(information, " ") + +# +# this function triggers a test. +# +def Test(server, capability, arguments): + snapshot = None + suite = None + environment = None + identifier = None + + # warning + Warning() + + # check the arguments + if len(arguments) != 2: + Usage() + sys.exit(42) + + # retrieve the arguments. + environment = arguments[0] + suite = arguments[1] + + # display a message. + env.display(env.HEADER_OK, + "generating the kaneton snapshot", + env.OPTION_NONE) + + # export the current kaneton implementation. + env.launch(env._EXPORT_SCRIPT_, + "test:" + capability["type"], + env.OPTION_QUIET) + + # display a message. + env.display(env.HEADER_OK, + "loading the kaneton snapshot", + env.OPTION_NONE) + + # read the snapshot. + snapshot = env.pull(env._EXPORT_DIR_ + "/output/" + \ + "test:" + capability["type"] + ".tar.bz2", + env.OPTION_NONE) + + # display a message. + env.display(env.HEADER_OK, + "requesting the server", + env.OPTION_NONE) + + # trigger a test. + identifier = ktp.xmlrpc.Call(server.Test(capability, + ktp.miscellaneous.Binary(snapshot), + g_platform, + g_architecture, + environment, + suite)) + + # display the received report. + env.display(env.HEADER_OK, + "the snapshot has been scheduled for testing under the " \ + "identifier: " + identifier, + env.OPTION_NONE) + +# +# this function requests the server for re-testing a snapshot. +# +def Retest(server, capability, arguments): + snapshot = None + identifier = None + + # warning + Warning() + + # check the arguments + if len(arguments) != 1: + Usage() + sys.exit(42) + + # retrieve the arguments. + identifier = arguments[0] + + # display a message. + env.display(env.HEADER_OK, + "requesting the server", + env.OPTION_NONE) + + # trigger a test. + ktp.xmlrpc.Call(server.Retest(capability, + identifier)) + + # display a message. + env.display(env.HEADER_OK, + "the snapshot has been re-tested successfully", + env.OPTION_NONE) + +# +# this function submits a snapshot. +# +def Submit(server, capability, arguments): + snapshot = None + stage = None + + # warning + Warning() + + # check the arguments + if len(arguments) != 1: + Usage() + sys.exit(42) + + # retrieve the arguments. + stage = arguments[0] + + # check if there is a 'snapshot.tar.bz2' in the client/ directory. + if env.path(env._TEST_CLIENT_DIR_ + "/snapshot.tar.bz2", + env.OPTION_EXIST): + # display a message. + env.display(env.HEADER_OK, + "loading the snapshot '" + \ + env._TEST_CLIENT_DIR_ + "/snapshot.tar.bz2'", + env.OPTION_NONE) + + # read the snapshot. + snapshot = env.pull(env._TEST_CLIENT_DIR_ + "/snapshot.tar.bz2", + env.OPTION_NONE) + else: + # display a message. + env.display(env.HEADER_OK, + "generating the kaneton snapshot", + env.OPTION_NONE) + + # export the current kaneton implementation. + env.launch(env._EXPORT_SCRIPT_, + "test:" + capability["type"], + env.OPTION_QUIET) + + # display a message. + env.display(env.HEADER_OK, + "loading the kaneton snapshot", + env.OPTION_NONE) + + # read the snapshot. + snapshot = env.pull(env._EXPORT_DIR_ + "/output/" + \ + "test:" + capability["type"] + ".tar.bz2", + env.OPTION_NONE) + + # display a message. + env.display(env.HEADER_OK, + "requesting the server", + env.OPTION_NONE) + + # submit the snapshot. + ktp.xmlrpc.Call(server.Submit(capability, + ktp.miscellaneous.Binary(snapshot), + stage)) + + # display a message. + env.display(env.HEADER_OK, + "the snapshot has been submitted successfully", + env.OPTION_NONE) + +# +# this is the main function. +# +def Main(): + global g_server + global g_capability + global g_platform + global g_architecture + + server = None + capability = None + command = None + arguments = None + + # retrieve the command. + if len(sys.argv) != 2: + Usage() + sys.exit(42) + + # set the command. + command = sys.argv[1].split("-")[0] + + # set the variables. + g_server = env._TEST_SERVER_ + g_capability = env._TEST_CAPABILITY_ + g_platform = env._TEST_PLATFORM_ + g_architecture = env._TEST_ARCHITECTURE_ + + # connect to the server. + server = ktp.xmlrpc.Connect(g_server) + + # load the student capability. + capability = ktp.capability.Load(g_capability) + + # trigger the appropriate command. + for name in c_commands: + if command == name.split("-")[0]: + # set the arguments. + if len(sys.argv[1].split("-")) > 1: + arguments = sys.argv[1].split("-")[1].split("::") + else: + arguments = [] + + c_commands[name](server, capability, arguments) + + sys.exit(0) + + # wrong command. + env.display(env.HEADER_ERROR, + "unknown command '" + arguments[0] + "'", + env.OPTION_NONE) + + # display usage. + Usage() + +# +# ---------- constants -------------------------------------------------------- +# + +c_commands = { + "information": Information, + "test-[environment]::[suite]": Test, + "retest-[identifier]": Retest, + "submit-[stage]": Submit +} + +# +# ---------- entry point ------------------------------------------------------ +# + +if __name__ == '__main__': + Main() diff --git a/test/packages/Makefile b/test/packages/Makefile new file mode 100644 index 0000000..62c8a9c --- /dev/null +++ b/test/packages/Makefile @@ -0,0 +1,43 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton.STABLE/test/package/Makefile +# +# created julien quintard [wed may 16 18:08:19 2007] +# updated julien quintard [mon oct 25 20:53:32 2010] +# + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear + +# +# ---------- variables -------------------------------------------------------- +# + +SUBDIRS := ktp + +# +# ---------- rules ------------------------------------------------------------ +# + +main: + +clear: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,clear,) ; \ + done + + $(call env_purge) diff --git a/test/packages/ktp/Makefile b/test/packages/ktp/Makefile new file mode 100644 index 0000000..abb4de2 --- /dev/null +++ b/test/packages/ktp/Makefile @@ -0,0 +1,33 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton.STABLE/test/package/ktp/Makefile +# +# created julien quintard [wed may 16 18:08:19 2007] +# updated julien quintard [mon oct 25 20:53:04 2010] +# + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../../../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear + +# +# ---------- rules ------------------------------------------------------------ +# + +main: + +clear: + $(call env_purge) diff --git a/test/packages/ktp/__init__.py b/test/packages/ktp/__init__.py new file mode 100644 index 0000000..fbd05ca --- /dev/null +++ b/test/packages/ktp/__init__.py @@ -0,0 +1,58 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/KANETON-TEST-SYSTEM/packages/ktp/__init__.py +# +# created julien quintard [mon oct 25 19:43:41 2010] +# updated julien quintard [wed feb 16 13:31:24 2011] +# + +# +# ---------- information ------------------------------------------------------ +# +# the kaneton test package contains everything necessary for the scripts +# to perform cryptographic tasks, issuing remote requests and so on. +# +# note that this package is even more important for the server script which +# evolves outside the kaneton environment, hence does not have access to +# the kaneton environment functionalities. this is why this package provides +# features for reading and writing files for instance. +# + +# +# ---------- definitions ------------------------------------------------------ +# + +StatusOk = True +StatusError = False +StatusUnknown = -666 + +# +# ---------- modules ---------------------------------------------------------- +# + +import bulletin +import capability +import certificate +import code +import configuration +import database +import environment +import hook +import key +import log +import manifest +import miscellaneous +import process +import queue +import report +import snapshot +import stage +import statement +import suite +import trace +import xmlrpc diff --git a/test/packages/ktp/bulletin.py b/test/packages/ktp/bulletin.py new file mode 100644 index 0000000..fec9c3c --- /dev/null +++ b/test/packages/ktp/bulletin.py @@ -0,0 +1,41 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/KANETON-TEST-SYSTEM/packages/ktp/bulletin.py +# +# created julien quintard [mon oct 25 20:23:05 2010] +# updated julien quintard [wed oct 27 13:08:57 2010] +# + +# +# ---------- packages --------------------------------------------------------- +# + +import yaml + +# +# ---------- definitions ------------------------------------------------------ +# + +Extension = ".blt" + +# +# ---------- functions -------------------------------------------------------- +# + +# +# this function stores a bulletin. +# +def Store(bulletin, path): + yaml.dump(bulletin, + file(path, 'w')) + +# +# this function loads a bulletin. +# +def Load(path): + return yaml.load(file(path, 'r')) diff --git a/test/packages/ktp/capability.py b/test/packages/ktp/capability.py new file mode 100644 index 0000000..c6ee712 --- /dev/null +++ b/test/packages/ktp/capability.py @@ -0,0 +1,111 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/test/packages/ktp/capability.py +# +# created julien quintard [mon oct 25 19:58:10 2010] +# updated julien quintard [wed feb 9 06:42:08 2011] +# + +# +# ---------- packages --------------------------------------------------------- +# + +from OpenSSL import crypto + +import os +import hmac +import pickle +import re + +# +# ---------- definitions ------------------------------------------------------ +# + +Extension = ".cap" + +TypeContributor = "contributor" +TypeGroup = "group" +TypeStudent = "student" + +# +# ---------- functions -------------------------------------------------------- +# + +# +# this function returns a list of capability identifiers. +# +def List(directory): + capabilities = [] + entries = None + entry = None + path = None + + entries = os.listdir(directory) + + for entry in entries: + path = directory + "/" + entry + + if os.path.isfile(path) and re.search("^.*" + Extension + "$", path): + capabilities += [ entry[:-len(Extension)] ] + + return capabilities + +# +# this function creates a capability. +# +def Create(code, + identifier, + type, + attributes, + members): + h = hmac.new(code, pickle.dumps( (identifier, + type, + str(attributes), + str(members)) )) + token = h.hexdigest() + + capability = { "identifier": identifier, + "type": type, + "attributes": attributes, + "members": members, + "token": token } + + return capability + +# +# this function validates the capability according to the given +# key that has been used for issuing this capability. +# +def Validate(code, + capability): + h = hmac.new(code, + pickle.dumps( (capability["identifier"], + capability["type"], + str(capability["attributes"]), + str(capability["members"]) ))) + token = h.hexdigest() + + if token != capability["token"]: + return False + + return True + +# +# this function stores a capability on the file system. +# +def Store(path, + capability): + open(path, 'w').write(pickle.dumps(capability)) + +# +# this function loads a capability from the file system. +# +def Load(path): + capability = pickle.loads(open(path, 'r').read()) + + return capability diff --git a/test/packages/ktp/certificate.py b/test/packages/ktp/certificate.py new file mode 100644 index 0000000..ae1278e --- /dev/null +++ b/test/packages/ktp/certificate.py @@ -0,0 +1,84 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/KANETON-TEST-SYSTEM/packages/ktp/certificate.py +# +# created julien quintard [mon oct 25 20:01:03 2010] +# updated julien quintard [wed oct 27 13:10:06 2010] +# + +# +# ---------- packages --------------------------------------------------------- +# + +from OpenSSL import crypto + +# +# ---------- definitions ------------------------------------------------------ +# + +Extension = ".crt" + +# +# ---------- functions -------------------------------------------------------- +# + +# +# this function returns a certificate request. +# +def Request(key, + digest = "md5", + **attributes): + request = crypto.X509Req() + + subject = request.get_subject() + + for (k, v) in attributes.items(): + setattr(subject, k, v) + + request.set_pubkey(key) + request.sign(key, digest) + + return request + +# +# this function creates a certificate according to the given +# certificate request. +# +def Create(request, + issuer, + serial, + timestamp, + digest = "md5"): + issuer_certificate = None + issuer_key = None + timestamp_notbefore = None + timestamp_notafter = None + + (issuer_certificate, issuer_key) = issuer + (timestamp_notbefore, timestamp_notafter) = timestamp + + certificate = crypto.X509() + + certificate.set_serial_number(serial) + certificate.gmtime_adj_notBefore(timestamp_notbefore) + certificate.gmtime_adj_notAfter(timestamp_notafter) + certificate.set_issuer(issuer_certificate.get_subject()) + certificate.set_subject(request.get_subject()) + certificate.set_pubkey(request.get_pubkey()) + + certificate.sign(issuer_key, digest) + + return certificate + +# +# this function stores the given certificate on the file system. +# +def Store(path, + certificate): + open(path, + 'w').write(crypto.dump_certificate(crypto.FILETYPE_PEM, certificate)) diff --git a/test/packages/ktp/code.py b/test/packages/ktp/code.py new file mode 100644 index 0000000..9a10606 --- /dev/null +++ b/test/packages/ktp/code.py @@ -0,0 +1,55 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton.STABLE/test/packages/ktp/code.py +# +# created julien quintard [mon oct 25 20:03:52 2010] +# updated julien quintard [sat oct 30 11:17:56 2010] +# + +# +# ---------- packages --------------------------------------------------------- +# + +import random +import string + +# +# ---------- definitions ------------------------------------------------------ +# + +Extension = ".code" + +# +# ---------- functions -------------------------------------------------------- +# + +# +# this function generates a random code. +# +def Generate(): + random.seed() + + code = "".join([ random.choice(string.ascii_letters + string.punctuation) + for x in range(128) ]) + + return code + +# +# this function stores a code on the file system. +# +def Store(path, + code): + open(path, 'w').write(code) + +# +# this function reads a code file from the file system +# +def Load(path): + code = open(path, 'r').read() + + return code diff --git a/test/packages/ktp/configuration.py b/test/packages/ktp/configuration.py new file mode 100644 index 0000000..1431059 --- /dev/null +++ b/test/packages/ktp/configuration.py @@ -0,0 +1,34 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/KANE...EST-SYSTEM/packages/ktp/configuration.py +# +# created julien quintard [mon oct 25 20:22:05 2010] +# updated julien quintard [wed oct 27 13:11:00 2010] +# + +# +# ---------- packages --------------------------------------------------------- +# + +import yaml + +# +# ---------- definitions ------------------------------------------------------ +# + +Extension = ".conf" + +# +# ---------- functions -------------------------------------------------------- +# + +# +# this function loads a configuration. +# +def Load(path): + return yaml.load(file(path, 'r')) diff --git a/test/packages/ktp/database.py b/test/packages/ktp/database.py new file mode 100644 index 0000000..f21229d --- /dev/null +++ b/test/packages/ktp/database.py @@ -0,0 +1,68 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/KANETON-TEST-SYSTEM/packages/ktp/database.py +# +# created julien quintard [mon oct 25 20:23:05 2010] +# updated julien quintard [thu feb 3 23:58:14 2011] +# + +# +# ---------- packages --------------------------------------------------------- +# + +import yaml +import os +import re + +# +# ---------- definitions ------------------------------------------------------ +# + +Extension = ".db" + +# +# ---------- functions -------------------------------------------------------- +# + +# +# this function returns a list of database identifiers. +# +def List(directory): + databases = [] + entries = None + entry = None + path = None + + entries = os.listdir(directory) + + for entry in entries: + path = directory + "/" + entry + + if os.path.isfile(path) and re.search("^.*" + Extension + "$", path): + databases += [ entry[:-len(Extension)] ] + + return databases + +# +# this function generates a database based on the given configuration. +# +def Generate(configuration): + return configuration + +# +# this function stores a database. +# +def Store(database, path): + yaml.dump(database, + file(path, 'w')) + +# +# this function loads a database. +# +def Load(path): + return yaml.load(file(path, 'r')) diff --git a/test/packages/ktp/environment.py b/test/packages/ktp/environment.py new file mode 100644 index 0000000..c723470 --- /dev/null +++ b/test/packages/ktp/environment.py @@ -0,0 +1,65 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/KANETON-TEST-SYSTEM/packages/ktp/environment.py +# +# created julien quintard [mon oct 25 20:23:05 2010] +# updated julien quintard [wed feb 2 22:57:01 2011] +# + +# +# ---------- packages --------------------------------------------------------- +# + +import yaml +import os +import re + +# +# ---------- definitions ------------------------------------------------------ +# + +Extension = ".env" + +QEMU = "qemu" +Xen = "xen" + +# +# ---------- functions -------------------------------------------------------- +# + +# +# this function returns a list of environment identifiers. +# +def List(directory): + environments = [] + entries = None + entry = None + path = None + + entries = os.listdir(directory) + + for entry in entries: + path = directory + "/" + entry + + if os.path.isfile(path) and re.search("^.*" + Extension + "$", path): + environments += [ entry[:-len(Extension)] ] + + return environments + +# +# this function stores a environment. +# +def Store(environment, path): + yaml.dump(environment, + file(path, 'w')) + +# +# this function loads a environment. +# +def Load(path): + return yaml.load(file(path, 'r')) diff --git a/test/packages/ktp/hook.py b/test/packages/ktp/hook.py new file mode 100644 index 0000000..6a7d5b9 --- /dev/null +++ b/test/packages/ktp/hook.py @@ -0,0 +1,37 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/KANETON-TEST-SYSTEM/packages/ktp/hook.py +# +# created julien quintard [mon oct 25 19:58:10 2010] +# updated julien quintard [thu feb 3 23:56:36 2011] +# + +# +# ---------- packages --------------------------------------------------------- +# + +import os +import yaml + +# +# ---------- definitions ------------------------------------------------------ +# + +Extension = ".hk" + +# +# ---------- functions -------------------------------------------------------- +# + +# +# this function loads the hooks dispatcher. +# +def Load(directory): + path = directory + "/hooks" + Extension + + return yaml.load(file(path, 'r')) diff --git a/test/packages/ktp/key.py b/test/packages/ktp/key.py new file mode 100644 index 0000000..e52a0be --- /dev/null +++ b/test/packages/ktp/key.py @@ -0,0 +1,47 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/KANETON-TEST-SYSTEM/packages/ktp/key.py +# +# created julien quintard [mon oct 25 20:11:55 2010] +# updated julien quintard [wed oct 27 13:11:59 2010] +# + +# +# ---------- packages --------------------------------------------------------- +# + +from OpenSSL import crypto + +# +# ---------- definitions ------------------------------------------------------ +# + +Extension = ".key" + +# +# ---------- functions -------------------------------------------------------- +# + +# +# this function generates a cryptographic key. +# +def Generate(type = crypto.TYPE_RSA, + length = 2048): + key = crypto.PKey() + + key.generate_key(type, length) + + return key + +# +# this function stores a key. +# +def Store(path, + key): + open(path, + 'w').write(crypto.dump_privatekey(crypto.FILETYPE_PEM, key)) diff --git a/test/packages/ktp/log.py b/test/packages/ktp/log.py new file mode 100644 index 0000000..a7c4d68 --- /dev/null +++ b/test/packages/ktp/log.py @@ -0,0 +1,50 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/KANETON-TEST-SYSTEM/packages/ktp/log.py +# +# created julien quintard [mon oct 25 19:58:10 2010] +# updated julien quintard [tue mar 8 11:49:26 2011] +# + +# +# ---------- packages --------------------------------------------------------- +# + +import os +import time +import fcntl + +# +# ---------- definitions ------------------------------------------------------ +# + +Extension = ".log" + +# +# ---------- functions -------------------------------------------------------- +# + +# +# this function records the given message within the current log file. +# +def Record(directory, message): + handle = None + header = None + path = None + + path = directory + "/" + time.strftime("%Y%m%d") + Extension + + header = time.strftime("[%Y/%m/%d %H:%M:%S] ") + + handle = open(path, "a") + + fcntl.flock(handle.fileno(), fcntl.LOCK_EX) + + handle.write(header + message + "\n") + + handle.close() diff --git a/test/packages/ktp/manifest.py b/test/packages/ktp/manifest.py new file mode 100644 index 0000000..0eee520 --- /dev/null +++ b/test/packages/ktp/manifest.py @@ -0,0 +1,34 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/KANETON-TEST-SYSTEM/packages/ktp/manifest.py +# +# created julien quintard [wed oct 27 10:53:58 2010] +# updated julien quintard [tue dec 7 13:41:02 2010] +# + +# +# ---------- packages --------------------------------------------------------- +# + +import yaml + +# +# ---------- definitions ------------------------------------------------------ +# + +Extension = ".mnf" + +# +# ---------- functions -------------------------------------------------------- +# + +# +# this function loads a manifest. +# +def Load(path): + return yaml.load(file(path, 'r')) diff --git a/test/packages/ktp/miscellaneous.py b/test/packages/ktp/miscellaneous.py new file mode 100644 index 0000000..32e0201 --- /dev/null +++ b/test/packages/ktp/miscellaneous.py @@ -0,0 +1,282 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/KANE...EST-SYSTEM/packages/ktp/miscellaneous.py +# +# created julien quintard [mon oct 25 19:51:49 2010] +# updated julien quintard [sun feb 20 01:00:57 2011] +# + +# +# ---------- packages --------------------------------------------------------- +# + +import xmlrpclib +import tempfile +import os +import shutil +import re + +import ktp + +# +# ---------- definitions ------------------------------------------------------ +# + +OptionNone = 0 +OptionFile = 1 +OptionDirectory = 2 +OptionRecursive = 3 + +# +# ---------- functions -------------------------------------------------------- +# + +# +# this function converts some data into its binary form. +# +def Binary(data): + return xmlrpclib.Binary(data) + +# +# this function removes the target file system object. +# +def Remove(target): + entries = None + entry = None + + if os.path.isfile(target) or os.path.islink(target): + os.unlink(target) + + if os.path.isdir(target): + entries = os.listdir(target) + + for entry in entries: + Remove(target + "/" + entry) + + os.rmdir(target) + +# +# this function writes data to a file. +# +def Push(content, path): + handle = None + + handle = open(path, "w") + + handle.write(content) + + handle.close() + +# +# this function reads data from a file. +# +def Pull(path): + handle = None + line = None + + if not os.path.exists(path): + return None + + try: + handle = open(path, "r") + except IOError: + return None + + content = str() + + for line in handle.readlines(): + content += line + + handle.close() + + if len(content) == 0: + content = None + + return content + +# +# this function creates a temporary file or directory. +# +def Temporary(option): + location = None + + if option == OptionFile: + tuple = tempfile.mkstemp() + + os.close(tuple[0]) + + location = tuple[1] + elif option == OptionDirectory: + location = tempfile.mkdtemp() + + return location + +# +# this function copies one file to another target. +# +def Copy(path, target): + shutil.copy(path, target) + +# +# this function searches for file names matching the given pattern. +# +def Search(directory, pattern, options): + elements = [] + entries = None + entry = None + + entries = os.listdir(directory) + + for entry in entries: + if (options & OptionFile) and \ + (os.path.isfile(directory + "/" + entry)) and \ + (re.search(pattern, entry)): + elements += [ directory + "/" + entry ] + + if (os.path.isdir(directory + "/" + entry)): + if (options & OptionDirectory) and \ + (re.search(pattern, entry)): + elements += [ directory + "/" + entry ] + + if (options & OptionRecursive) and \ + (not os.path.islink(directory + "/" + entry)): + elements += Search(directory + "/" + entry, pattern, options) + + return elements + +# +# this function creates the directories up to the destination path. +# +def Dig(target): + path = None + steps = None + step = None + + if target[0] == "/": + path = "/" + + steps = os.path.dirname(target).strip("/").split("/") + + for step in steps: + if not path: + path = step + else: + path = path + "/" + step + + if not os.path.exists(path): + os.mkdir(path) + +# +# this function transfers the content from one file to the other. +# +def Transfer(source, destination): + input = None + output = None + line = None + + try: + input = open(source, "r") + output = open(destination, "a") + except IOError: + return None + + line = input.read(256) + output.write(line) + + output.close() + input.close() + +def Email(source, + destination, + subject, + body, + attachment = None): + capability = None + content = None + configuration = None + message = None + stream = None + report = None + status = None + output = None + emails = None + a = None + + # create a temporary file. + configuration = ktp.miscellaneous.Temporary(ktp.miscellaneous.OptionFile) + + # create a configuration file. + content = """\ +set realname = "%(from)s" +set from = "%(from)s" +set use_from = yes\ +""" % { "from": source } + + # store the temporary mutt configuration file. + ktp.miscellaneous.Push(content, configuration) + + # create a temporary file. + message = ktp.miscellaneous.Temporary(ktp.miscellaneous.OptionFile) + + # prepare the -a option depending on the presence of an attachment. + if attachment: + a = [ "-a", attachment ] + else: + a = [] + + # store the temporary message. + ktp.miscellaneous.Push(body, message) + + # create a stream file. + stream = ktp.miscellaneous.Temporary(ktp.miscellaneous.OptionFile) + + # email the capability to the supposed recipient. + status = ktp.process.Invoke("mutt", + a + + [ "-F", configuration, + "-s", subject, + " ".join(destination), + "<" + message ]) + + # retrieve the output. + output = ktp.miscellaneous.Pull(stream) + + # remove the stream file. + ktp.miscellaneous.Remove(stream) + + # remove the temporary files. + ktp.miscellaneous.Remove(message) + ktp.miscellaneous.Remove(configuration) + +# +# this function transforms a Python data structure into a string +# representation. +# +def Stringify(data, margin = "", alignment = 26): + string = str() + key = None + length = None + + if isinstance(data, dict): + for key in data: + if not isinstance(data[key], dict) and not isinstance(data[key], list): + length = len(margin) + len(str(key)) + 1 + + string += margin + str(key) + ":" + \ + (alignment - length) * " " + str(data[key]) + "\n" + else: + string += margin + str(key) + ":" + "\n" + + string += Stringify(data[key], margin + " ", alignment) + elif isinstance(data, list): + for element in data: + string += Stringify(element, margin, alignment) + else: + string += alignment * " " + str(data) + "\n" + + return string diff --git a/test/packages/ktp/process.py b/test/packages/ktp/process.py new file mode 100644 index 0000000..f13841e --- /dev/null +++ b/test/packages/ktp/process.py @@ -0,0 +1,173 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/KANE...TEST-SYSTEM/test/packages/ktp/process.py +# +# created julien quintard [mon oct 25 20:54:05 2010] +# updated julien quintard [thu nov 4 14:40:44 2010] +# + +# +# ---------- packages --------------------------------------------------------- +# + +import subprocess +import os + +import miscellaneous +import ktp + +# +# ---------- definitions ------------------------------------------------------ +# + +OptionNone = 0 +OptionBackground = 1 + +# +# ---------- functions -------------------------------------------------------- +# + +# +# this function invokes a sub-process and returns either the status or a +# monitor if the invocation has been performed in background. +# +def Invoke(command, + arguments, + stream = None, + option = OptionNone): + info = None + directory = None + file = None + handle = None + descriptor = None + status = None + output = None + error = None + + # retrive file information. + info = os.path.split(command) + + directory = info[0] + file = info[1] + + # change directory if required. + if directory: + wd = os.getcwd() + os.chdir(directory) + + # set up the stdout and stderr, according to the given stream file. + if stream: + # open the stream file. + descriptor = os.open(stream, os.O_CREAT | os.O_TRUNC | os.O_WRONLY) + + # set the output and error descriptors. + output = descriptor + error = subprocess.STDOUT + else: + # open the null stream file. + descriptor = os.open(os.devnull, os.O_WRONLY) + + # set the output and error descriptors so that to ignore everything. + output = descriptor + error = subprocess.STDOUT + + # launch the command. + handle = subprocess.Popen(command + " " + " ".join(arguments), + stdout = output, + stderr = error, + shell = True) + + # come back to the original directory if necessary. + if directory: + os.chdir(wd) + + # return information according to the option. + if option == OptionBackground: + return (handle, stream, descriptor) + else: + # wait for the process to terminate. + status = handle.wait() + + # close the descriptor. + if descriptor: + os.close(descriptor) + + if status != 0: + return (ktp.StatusError) + else: + return (ktp.StatusOk) + +# +# this function waits for a process to terminate and return the status. +# +def Wait(monitor): + handle = None + stream = None + descriptor = None + status = None + + # retrieve the information. + (handle, stream, descriptor) = monitor + + # wait for the process to terminate. + status = handle.wait() + + # close the descriptor. + if descriptor: + os.close(descriptor) + + if status != 0: + return (ktp.StatusError) + else: + return (ktp.StatusOk) + +# +# this function probes a process. +# +def Probe(monitor): + handle = None + stream = None + descriptor = None + status = None + + # retrieve the information. + (handle, stream, descriptor) = monitor + + # probe the process. + status = handle.poll() + + if isinstance(status, int): + # close the descriptor. + if descriptor: + os.close(descriptor) + + if status != 0: + return (ktp.StatusError) + else: + return (ktp.StatusOk) + else: + return (ktp.StatusUnknown) + +# +# this function terminates a process by killing it. +# +def Terminate(monitor): + handle = None + stream = None + descriptor = None + status = None + + # retrieve the information. + (handle, stream, descriptor) = monitor + + # kill the process. + handle.kill() + + # close the descriptor. + if descriptor: + os.close(descriptor) diff --git a/test/packages/ktp/queue.py b/test/packages/ktp/queue.py new file mode 100644 index 0000000..9681b1b --- /dev/null +++ b/test/packages/ktp/queue.py @@ -0,0 +1,59 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/KANETON-TEST-SYSTEM/packages/ktp/queue.py +# +# created julien quintard [mon oct 25 19:58:10 2010] +# updated julien quintard [wed feb 2 20:59:25 2011] +# + +# +# ---------- packages --------------------------------------------------------- +# + +import os + +import ktp + +# +# ---------- functions -------------------------------------------------------- +# + +# +# this function adds an identifier to the queue. +# +def Push(directory, + identifier): + handle = None + path = None + + path = directory + "/" + identifier + + ktp.miscellaneous.Push("", + path) + +# +# this function returns the next queue'd identifier, or None if none. +# +def Pop(directory): + identifier = None + entries = None + entry = None + path = None + + entries = os.listdir(directory) + + for entry in entries: + path = directory + "/" + entry + + identifier = entry + + ktp.miscellaneous.Remove(path) + + return identifier + + return None diff --git a/test/packages/ktp/report.py b/test/packages/ktp/report.py new file mode 100644 index 0000000..8323414 --- /dev/null +++ b/test/packages/ktp/report.py @@ -0,0 +1,67 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/KANETON-TEST-SYSTEM/packages/ktp/report.py +# +# created julien quintard [tue oct 26 11:12:10 2010] +# updated julien quintard [wed feb 2 14:41:38 2011] +# + +# +# ---------- packages --------------------------------------------------------- +# + +import yaml +import os +import re + +import miscellaneous + +# +# ---------- definitions ------------------------------------------------------ +# + +Extension = ".rpt" + +StateInProgress = "in progress" +StateDone = "done" + +# +# ---------- functions -------------------------------------------------------- +# + +# +# this function returns a list of report identifiers. +# +def List(directory): + reports = [] + entries = None + entry = None + path = None + + entries = os.listdir(directory) + + for entry in entries: + path = directory + "/" + entry + + if os.path.isfile(path) and re.search("^.*" + Extension + "$", path): + reports += [ entry[:-len(Extension)] ] + + return reports + +# +# this function stores a report. +# +def Store(report, path): + yaml.dump(report, + file(path, 'w')) + +# +# this function loads a report. +# +def Load(path): + return yaml.load(file(path, 'r')) diff --git a/test/packages/ktp/snapshot.py b/test/packages/ktp/snapshot.py new file mode 100644 index 0000000..a086576 --- /dev/null +++ b/test/packages/ktp/snapshot.py @@ -0,0 +1,18 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/KANETON-TEST-SYSTEM/packages/ktp/snapshot.py +# +# created julien quintard [wed oct 27 14:09:12 2010] +# updated julien quintard [wed oct 27 14:09:21 2010] +# + +# +# ---------- definitions ------------------------------------------------------ +# + +Extension = ".snp" diff --git a/test/packages/ktp/stage.py b/test/packages/ktp/stage.py new file mode 100644 index 0000000..aaca2bf --- /dev/null +++ b/test/packages/ktp/stage.py @@ -0,0 +1,62 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/KANETON-TEST-SYSTEM/packages/ktp/stage.py +# +# created julien quintard [mon oct 25 20:23:05 2010] +# updated julien quintard [sun dec 5 22:15:02 2010] +# + +# +# ---------- packages --------------------------------------------------------- +# + +import yaml +import os +import re + +# +# ---------- definitions ------------------------------------------------------ +# + +Extension = ".stg" + +# +# ---------- functions -------------------------------------------------------- +# + +# +# this function returns a list of stage identifiers. +# +def List(directory): + stages = [] + entries = None + entry = None + path = None + + entries = os.listdir(directory) + + for entry in entries: + path = directory + "/" + entry + + if os.path.isfile(path) and re.search("^.*" + Extension + "$", path): + stages += [ entry[:-len(Extension)] ] + + return stages + +# +# this function stores a stage. +# +def Store(stage, path): + yaml.dump(stage, + file(path, 'w')) + +# +# this function loads a stage. +# +def Load(path): + return yaml.load(file(path, 'r')) diff --git a/test/packages/ktp/statement.py b/test/packages/ktp/statement.py new file mode 100644 index 0000000..98c6b22 --- /dev/null +++ b/test/packages/ktp/statement.py @@ -0,0 +1,62 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/KANETON-TEST-SYSTEM/packages/ktp/statement.py +# +# created julien quintard [mon oct 25 20:23:05 2010] +# updated julien quintard [mon dec 6 07:45:36 2010] +# + +# +# ---------- packages --------------------------------------------------------- +# + +import yaml +import os +import re + +# +# ---------- definitions ------------------------------------------------------ +# + +Extension = ".stm" + +# +# ---------- functions -------------------------------------------------------- +# + +# +# this function returns a list of statement identifiers. +# +def List(directory): + statements = [] + entries = None + entry = None + path = None + + entries = os.listdir(directory) + + for entry in entries: + path = directory + "/" + entry + + if os.path.isfile(path) and re.search("^.*" + Extension + "$", path): + statements += [ entry[:-len(Extension)] ] + + return statements + +# +# this function stores a statement. +# +def Store(statement, path): + yaml.dump(statement, + file(path, 'w')) + +# +# this function loads a statement. +# +def Load(path): + return yaml.load(file(path, 'r')) diff --git a/test/packages/ktp/suite.py b/test/packages/ktp/suite.py new file mode 100644 index 0000000..011d162 --- /dev/null +++ b/test/packages/ktp/suite.py @@ -0,0 +1,106 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/KANETON-TEST-SYSTEM/packages/ktp/suite.py +# +# created julien quintard [wed oct 27 10:53:58 2010] +# updated julien quintard [tue dec 7 15:09:17 2010] +# + +# +# ---------- packages --------------------------------------------------------- +# + +import yaml +import os +import re +import sys + +TestDirectory = os.path.abspath(os.path.dirname( \ + os.path.realpath(sys.argv[0])) + "/..") + +sys.path.append(TestDirectory + "/packages") + +import ktp + +# +# ---------- definitions ------------------------------------------------------ +# + +Extension = ".suite" + +TestsDirectory = TestDirectory + "/tests" + +# +# ---------- functions -------------------------------------------------------- +# + +# +# this function returns a list of suite identifiers. +# +def List(directory): + suites = [] + entries = None + entry = None + path = None + + entries = os.listdir(directory) + + for entry in entries: + path = directory + "/" + entry + + if os.path.isfile(path) and re.search("^.*" + Extension + "$", path): + suites += [ entry[:-len(Extension)] ] + + return suites + +# +# this function loads a suite. +# +def Load(path): + return yaml.load(file(path, 'r')) + +# +# this function loads all the manifests belonging to the given suite in +# a dictionary. +# +def Manifests(suite): + manifests = {} + component = None + name = None + location = None + paths = None + path = None + manifest = None + test = None + + # load the manifests. + if "components" in suite: + for component in suite["components"]: + for location in suite["components"][component]: + # search for manifest files. + paths = ktp.miscellaneous.Search(TestsDirectory + "/" + \ + location, + "^.*\.mnf$", + ktp.miscellaneous.OptionFile | + ktp.miscellaneous.OptionRecursive) + + for path in paths: + # load the manifest. + manifest = ktp.manifest.Load(path) + + # compute the proper name according to the defined component: + # for instance, given the "segment" component, the test name + # segment/permissions/01 will be transformed into permissions/01 + if component == manifest["name"][:len(component)]: + test = manifest["name"][len(component) + 1:] + else: + test = manifest["name"] + + manifests[test] = manifest + + return manifests diff --git a/test/packages/ktp/trace.py b/test/packages/ktp/trace.py new file mode 100644 index 0000000..8294af7 --- /dev/null +++ b/test/packages/ktp/trace.py @@ -0,0 +1,46 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/KANETON-TEST-SYSTEM/server/ktp/trace.py +# +# created julien quintard [mon oct 25 20:46:33 2010] +# updated julien quintard [tue oct 26 15:42:17 2010] +# + +# +# ---------- packages --------------------------------------------------------- +# + +import traceback + +import miscellaneous + +# +# ---------- functions -------------------------------------------------------- +# + +# +# this function generates a stack trace. +# +def Generate(): + temporary = None + trace = None + + # create a temporary file. + temporary = miscellaneous.Temporary(miscellaneous.OptionFile) + + # put the trace in the temporary file. + traceback.print_exc(None, file(temporary, 'w')) + + # read the file. + trace = miscellaneous.Pull(temporary) + + # remove the temporary file. + miscellaneous.Remove(temporary) + + # return the trace. + return trace diff --git a/test/packages/ktp/xmlrpc.py b/test/packages/ktp/xmlrpc.py new file mode 100644 index 0000000..5d42944 --- /dev/null +++ b/test/packages/ktp/xmlrpc.py @@ -0,0 +1,60 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/test/packages/ktp/xmlrpc.py +# +# created julien quintard [mon oct 25 19:44:10 2010] +# updated julien quintard [tue feb 8 17:39:08 2011] +# + +# +# ---------- packages --------------------------------------------------------- +# + +import xmlrpclib +import sys + +import ktp + +# +# ---------- functions -------------------------------------------------------- +# + +# +# this method wraps a call to a remote method. +# +def Call(value): + type = None + data = None + + # retrieve the type. + type = value[0] + + # transform the data should it be in binary form. + if isinstance(value[1], xmlrpclib.Binary) == True: + data = str(value[1]) + else: + data = value[1] + + # display an error if the type indicates so. + if type == ktp.StatusError: + print("[error] " + data) + sys.exit(42) + + return data + +# +# this method connects to an XMLRPC server. +# +def Connect(server): + handle = None + + # issue the connection request. + handle = xmlrpclib.Server(server, + allow_none = True) + + return handle diff --git a/tool/Makefile b/tool/Makefile new file mode 100644 index 0000000..f6473a6 --- /dev/null +++ b/tool/Makefile @@ -0,0 +1,50 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/export/output/kaneton/tool/Makefile +# +# created julien quintard [fri nov 28 16:24:37 2008] +# updated julien quintard [sat feb 5 12:11:38 2011] +# + +# +# ---------- dependencies ----------------------------------------------------- +# + +include ../environment/env.mk + +# +# ---------- directives ------------------------------------------------------- +# + +.PHONY: main clear prototypes headers + +# +# ---------- variables -------------------------------------------------------- +# + +SUBDIRS := + +# +# ---------- rules ------------------------------------------------------------ +# + +main: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,,) ; \ + done + +clear: + for d in $(SUBDIRS) ; do \ + $(call env_launch,$${d}/Makefile,clear,) ; \ + done + + $(call env_purge,) + +prototypes: + +headers: diff --git a/tool/mbl/grub/data/kaneton.img b/tool/mbl/grub/data/kaneton.img new file mode 100644 index 0000000000000000000000000000000000000000..ebda68b51f526f34423dbe880c1d4b3323b369ae GIT binary patch literal 1474535 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�Y-jMJk> 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

F%q+OqK=ho$>wyp077 zQXm4!Kkq09mfH$ZYhE_Z9{L&zsu%cK6kFr*;@AzqCnBzlnHxKR^h^|7 zE$O7Ue*9cq1HkmphiP7m-wgPl3`=7*Wu|{ROnGcEaY?hL|H2q$M48Z+2oEoid|f0w zgcH(3D`ooBnz|rgm&H!?^yRT>9$yh#?D2}&bsk?8YxVd8u}+V#i7}mgYR$E=n-v0I z7u)3Vhhm@e`1;u2dE5~DH;)@*Kk~RK_PWQ-u`xCWwPsapipQ<786K~W(dH$eTGJML zpT`}sI*&VJR{=i?US(hrlVx$Sl*Rj`EVPU+iLnhX7y2@(KaGMfjj;_f{C@nx@?w~y z+5e->%_{i+QXBrmFxj0T`+j!=J@_0vpIfXh%V2W5`lyNZtd<(=-IXR z2?jOoSp}RCZ(a7*VWezbwrv<`pe}oLL^@)%X`$uU zY+PwApXq0$Eki>k8VOCWBHosJLpowQ)zV)N>4$~K70;1;(scU<`R*Gspdg)>hpIqJG8ljU{{x}E>a8*mxC6XSd?#I z)DeG8(mazrBlt$}v!&v_q^!Sr9P?#LCiN>Nc06MgJk(v(LR-E6jCyhR&b#~X9=JPu z_YNCRVPAYaj7h%Hw*rPIi<^536l~3w_6(G49Vlu(8s;f`7mXEiDUf<7g@;Xx3YEGw zJ7LqJBW-|1sF_R&j8l>&w~XC=7yh6&Zyx_$0HC7gp7TmgcgHZ=&7}pV`|2EG=H}8u z(;d&DHTT?HI@Wa0$|1tzk*50;)XCMXw{Y7!FlJ1=s4n}cR`kK6q$TZcMjckyAh|YK zYy|szHtaX)(^2R9R-i%E6m%5~9*Z}a+azs4F}w`^uxG;olRl9?Kn875ao-BK6AT_F z}{| zp8mqR?7z~9&AYGlDU0B5o=yfFvUIn3@L|JDm3s4b>#qBNA@>=92!E> zV69YS^aNi{!fY^wLXPICv;9trq7W>dW|y;HI+#xL&)Itprqhga_Qr$hG{>C1{$M)I zHfJw7m`?N4*;z{89^Z$IQ<85Ue+SWnOzrl<(lOV2^R2gS*gpi{w-gL6Kvmv40B%C$ zE7;Q)-!sMRy})eU5Ar^SQ!L^19nWN;XXtr;Om86?5xS?rZ!tOqH9~X?7+n?#fa3va zP;XK*=Ee7;dA(1X7m|`inqX4QEa;8DNqWfcFg>?P-@G}VMZ!L@J-$~v*F7{rJ9?&a z0zqyHN6t+hbF9|nOuo8kujSs`0cca_bWF|T??B}Tw+WeDm}-JKrl%rU%|;ax~m6MnRzPnqfPmB5(V$&T961^C4P|0clapt|}n1Kb+m zy94}afRQw@*>xW59X<#8Mw_-{3L{L?LzccWp!Wwj^mFRik-xoZ6#C9l=vA2Dxb%eK z-)rf#!~82?XNP$#CN0hm^LqqOkW|kUtag}d1yh3xyR3pw5u6mPGtrL-K27MK63I3_zGX%dbc(UMQi0%Z9>>CuXe*TwZ|6Qk){ZG$^T~z##jkh)b)3HOg z<2cA^&bf-RkKscZ^2iLV>}2=h-}>pq6YuZOz#$pB~nYLnfKvh8`cOcHh= zd%3)oLioxcXkW|gjQmsJB_gwPJP63o-!t3jd;;D$amB;2(K|g*o?zp8kn#K2y2tMB zmjqxOUWlF`HFo!B!}qQgGA@4?M;NeXs{QZ$9T3>FQ|1ZFpDt~`s`~R1Bqkd@nM}ucolDFb)9mFo)vX`Ls z25s!Vx&OUwjPW088(sKo@m%jIa8=x^@mFS(sMWN{Gn3C8D`UqUI;w+t|MCJ%@4P7I*yD#|{r4akcD zA-)LBCtKC|ccHqN?sgZp2+#9>013MGYAD>`$Pt}$?dhKdQH-XzI+p!|h9JFa;cBxw zhHf&;t2z)YJXs0_9`C0BQG-KhHn`)Z_%1K**;a~9W|$oJa|-Dw_w{VpCmJQmt=A~+ zlYU{JvczhXu92P#y4#|fMMd40h`gjC2NK;0_nOk~Dv_U5dcbMSqC)6EL_`)x%zyL` zwjaWrr&uO2l89SncI3jpLAcLAqVV@2{HyzxL2-qGLE>!q?v)BI_8{FSvES%E%IUWT z$4OennM##?QBo#rQX~lU?w=$xVVu${A2#6|oa);XhikT&TZ;|B)OIUg`3x4(#5<^n1C z=reeeP8-7-&P?sF=3N@EeMg=S2s_!=hU~mpE4n&|^@&3tH&h7Nd9wA-p1O#-U{*6> z=YgRGwtJ44e}ppMkAx0HPSbxc%o=^5P%WKeX?Fj3X<)taojn_NLWpdSb4he)_j-;r zpdQB{c4zhm9d@W@3elZO7c|48#EWkpyZaG&$y&V{TSXyM<1q?0E_O*85lW6b zRB1y)by3ULO{z?9{AF^;svp@`ysWy%NEa(Ye5m{7?A7x9_`2+mFk3}H zyXPSe6r(AGo)cOksY-+8K0eMZB|eFNXO{C|yrX$+9XJ7F>8-C6k3*JT>WjbRnt9KL zV$_k!(9CLT*|gdM9an001f&J!jOhlQotM=|a}T|S6~Cxyf+Xfv?${z>;BnT;8*1vaO5F(2WWsclB)8C3TS-d4_hM7cp{I_9r^5IjBe4 zmHqB87cf%4Cky@1aKAG01<-PODK30?Mh0EYy%8gG-7O>E?_y<1fRR4dkGL3I{YF3N zrR#O@9rax7DI~a~F-tEz+Ol@U=mvBw7phpEGgTm4H-HpOT+j=^kmol+I9qxR|lvbLo1Vs{@|sejIeW!>^EJ9QPi^mBQ)=8 zs-KQm>pq_r`B&!d#lmIT0mdlRbN3^Zq0b)dq1~e5*pq6$v58n`C0sZlY2k! zhx4#|KR?4SN4WQsUUAvz_ObLZ{N7JGdM7J++`XSnN_g*QZqjh?Cj*S6l`8}UH!RBM z#xgg!_md%T(KWjF6Ac|=b-eF?G1b(?JnXQj#fX4PCkUXTo`Eq?d7j8Nxvl4wG59-e z2ed%*;(NP_sY2b05tUbFqNAYpmgCOHWGY(6i7-OKb%leN48xzk7580N%+C;EY2QL7 zHCxoXxQNc2k;~+hv*-Q2i%Ri70_}Nr{I!;W6WEcl)k9D)9*(Z}$sUdPJ}kX2sB1x! zeKz|Gm7Xdfo#cXx8N|>Y%F_ee7hzNZ$+`gYic&<^HWBKco(+2>)IA#N9){YtVlU@# z9Nk-WLQj_JR;BdxPmr13gZH2Wdl!v|r_U3jUCQb~QMS?zuaHHrYE|!c*1JeTX0AN{ zhnHvZy!gJZlEDe|&|k!tg2AKZv+>C1;1kmFj*~8+DEk8+&Q7lK%(xGO7 z!h3UF5efkMiz$7}kTbsY;aVnJtfIldj21&)h+Q*ykyU;;9~`t#=O-vLY8+(FTihq1 z2+0M605A$ORDEN3A)AR@+1ln}7pEnDN-Jtg3I?x5Ha25J61$|u>?t`^V;vZR<8=Hj z=sq2Nj;L19K8}|9RD@&~YQ3 zH)Lrks;EH2!f4JmtlR*9A_@oWZ$kE^v9a=SP|M_Cf0Oc7s+6W|7zdbjGz42WPD4Z} zah(=nFI8?_FAIN=cv%z~T%D`y2wERBA9&;35yAKL7YiqQw4Shu9G~klMtSTSICL^& zu)cs6z7PvpGG{>xcl^nz%S^A;IXMmm_0*?VMr zKk3HKC-$q%6cjft8%exAhnOsCxu4XFbE$KCp|EA=ToBa> z^z+FuR75qI#7sz(sAiMg>cVsKU7}h>r%Rm^qN23e{Rnu?Eg9S^vVH8Ar0an=9Qzd@ zw(sp@zed_DqHlWFCxGY|HTm-oB`xY}D3OrU1A{-xl}K1rhCI&IzN$QM z2|?Xp>qSyNu35C1Q;7z-gjvmj?7ccd9zpnoDMPR6Q)Xjfn*PJ3Nu|y>`dW}Xg(rX&$d*+-z=)8K((_6 z3rdlqCySmVnhasK1UEM|8lW4;Oucc)hw&CZwLSha>M%xFFW1B;bdBBpBQ#U1XuTCm zJW&|mDoQ-%z%J30Mzbku1+BLDE^&%xaDkfS25J1@TR0K(3dOqY4TF}IJNx20Ax@kk zIX51ewbf;7hk5qgkv*g=op6E{=u3`vAHD?rXAnznl5H$M{!g1E>)FThiYVT9c_qfq zhy%BY(|~t+DROFtlhhncVZA#)wb6CD=yvpM*kQV{dLXTCM8Au3{rZ$$6vdI71{=rG z?zkN~pfd2B z_*1>{uMHk;Um%IU)))U;Z~O_FPd_o#{rph(v)R8pLYk?_^-*|ot){_x88MmFIbKGr zF30D18L_N?gA;BdG9(w5^{vSEuJ{VnIpO*N-oJuLR^N(&MZGJ&)EEC!Z~Wn&4G+s) zL0^yrhY?gAuumU{?W}lFBc#uO4-S+Qi z2+}3>9WO5EaTkqGorQ?dpWBY)4(;X92nz;}k(C5&8S62ngZ~_U)a8fGnAT!gjV{y2 zYK8fyoo-7yQXj~^TeF5N=xD`B+5_4BKeyG~x}cux_df*_xGT3C zvyD>L>Yfeb4Yw_+^ENuGHt#hzEY``6_rlX>+ya19W4aX{Rp{LY;yIttL3 zzDgSbqxP^Hdd>-~QY1wbfD-%Q5=4zUg=JSv65s&;d$9%X1mQ)Q?VZ&tj*Q4a>Ol4w z&1&{K1nRf2XF13eg(Z}eg{yfM=_!{OC$O;I)F$q2|FoTATcnFcnf$5%u>z|$*Z=~$O7P*)d5^Y-}bMbP+QGv3|kvT$?< zMN^E^I6TC|G|K5E)X5--dI~;!R0#PEg810m4dRrXASUQ%Z#4Jua8Ud=2yvge!^Xb1 zE;sg{vZ~qGF$yij9x#0KdUMZ)*N3_>;YaCUP*IevN46E;RE!=R71;$8YAm8E6e`a)*$@Q&K?>*z`nYph(Z3G7wxfCC*iA^P1XA+2;3?u2 z+Ug^3!6B$AbpC^+qWMAM=B?SHlE)#r81}I6n#V~9HXvO^L>R~LyTXIxc+*fdUx>uz zK%hjZPQx>2E8o<<_=HXI*P?0D6DS=&59`@OQ7DrpO2hv%_UvohMY=Y{A=-;eo{nQ( z{x0q4ZLZw*OB9Rgeh>Ek@O0r8ur>Q5t$YwWyLsL<8oEQ>l&Pjx2dlh1U4UUGw1wmC zf;cA(>$y3RY~}iK8Y|@cUz>0b9WYp&FRVF9lT%uk{eX>T8)ST}lnSrn)n(a5IaKxm zMT1X|kc2Zt*x|H8QOVP$E_?K_&zQU&67!7*$NXa}n{#7+K8LF7*iTCbvlmk?;o5C` zK0t=tu?t?A%k6gzSoGW-KOtgGxZ@=T#G@M%dcI9;)|aUB^v&Y-+5~cEeTja}rh|pj z*CuSgV}}jQeg?(KH|Tgr7WBSP(lxfN_meNk8w1|FthjgcHvxKg^vPc?P@31udp{|8 z`L-l3eO%uid>z}5vhBU&S-`<}_lb|&wi$@&-cOR&x0(On8S9hqHx=}K@=as7gVFZM z|C-^=l6tIdT!50$B##l&a}ZvV_P+Qj({}XUu@fKme4GE9^+#=&SEgDz(xT1s4hE@* zlDlt*Q|8;k+cXy2QImzyb=i}xQgqv9DUDdEEddQ-sOTOW1S;O*o(&UB8r=-r!GyZ( zPweWe|8x*LnpyXr8}8r&8~nOU!>M5!{_0@=ETo~Lo^MN0t}hsz3I8ECq_Tc!wC@I= zbIC@n9iHr|!@qLLE*b7%lWhjS-Y3j)a?CcPXpzY_=&`&19fJS4_;1nHMTg3677vE3 z+P-@BPf3s6eK$PW=8dzytn75gVWaq~VC<{9m+8imkA4XL`c_~T^U3GAaen|(0=rvw ze^@_fednI9J*K-5QO@Fm#11HYVmmgEHw*_0K0Vo4(CH6;8tD-4Xvo-Ob{v{Xk3mOT z+>#YvF<@QzePv7e|2p2QQSkZKv-1Pix$5RBi;do`ez4eG{|3wV#5b1 z`$=Ypow)h4oYmMUW8w_}D9j(hAne(kw(cLFPOsH?@ zGHg4*P*Hkn(G7F7^<&$BMDgkLs|edR16$|O6=-`&UxfL>n0T@Fg|hQ;Sy5~&h?o$? zw%r+0YK#FH4 zY7Z!*QS{``lJ;%gD&X>smLnsS{ju~J=#*o(VrUK}-%*3J@V6j+>fqET4`2qi2Rg;x zg2Bo1()Bzge^s=DYa?!;H|-=S8NJhtNXo>xxa`pVInT}N^U9H(U<{=VmxBe%X%l-adE zAnxyZ<$|rR6igl?v?uq2QSkk%NZ;%R;5~aVw333d^(Qh3@CCFLKS0`lI*bHx2O~#b zXf6Be@ErZRo>HXF=Ep4; z;lJz?+FCRiOwV1hbZx&`4$)6LxTycT8X5BdD#-ohB;nE9ULOP5}E0s1; z>aBxGPq}nXe{Y%OA0LBs)KNz6)bL^GTM&j4gd4n~!+Sgm0Wr^q98Sm?#k@sbl3P>O zm1wpoX$wRdfbqI{hJtFOXKF`xTbmwr9m(0uDzB)lnmuQ3^}L0P7T3lvTyoLUi{E$2 zvP<7zciH93uUK*ARUf$enh`&=#OV8mm5oh_=2b1NtJk!(cXTFi=t`x#*RH#9{Y`K7 zJ?_-PzoU#^#8rl?#a805I_s9!#wktnOa!MyB6s={&doDzH1Z z{zt($yNu_!BT1hi_$$&y&1eXc@%Yr8g-X?gG z;C~lE6Uvwi1kVw?Q1B&!mk8#6u8yq)o%QFR&@i7C zKi-S?^M|8Ht{c~4L^)F3dvX-|sn|q8|FgiZ^}rb$)i(mXH^6ju>8!$L?~&pP{oE7y z`A>`2puS&#)#41Shq}jwc@^j*h52<$pL8+~8DYsJW2U)JeR_I9=eK<;u@6Q3sFzjH zV`q%ky;}q;{daVFTx@Pm#(udR;>Pj6Td+F(RIu{D5Ms#rZx^im|5QN#HpElLTqGa! z(311N7@{g;E))87f(4pC3cglf@V`s{drsGM{uWtl1I}07BGzct4ER{W@JFGiL!M(fg9f~)1rg@We`zD}^p?HdKZSLmM*yio901uqu- zyx_RtU4oYij!k7;%LJb)SnFhs;N?R9fZ!E^(}J%O{0YGwf{VevjJZ|tP3R8Mc4B;4 zg6(rb*4R!Be3P^u*)JtvBN zvM~ zlMk#vsg{_G;kNnuI zj=>z}xXWRVwTL;^;@HX2IYx4rV9d zcKg2};hbUq9^pJI;Y^fpb_$*p$N_YK_Nhh8u5H$$wWA2-sl_}+dk@At-7N6*8Rim? zE6i0MSDKX`&xW$rn4clu48o_H`qzS|h8e*9d#1OJ=7HvzM&sMdySpCk}MfG~%- z6B@{z?$cw^1TyRn31dhIGqj!4r_)V(IP^K45D;k+Kte=il1VNhB3x7y5rvD2&2R}Q zDhdi-QKNzef?Sb+sF8f{yH-{0I-RugeM$cRUZL`w^t)HpUbSjf)v9q<0k`2t$FvY{ zMEFuuYj996J_w;sY66|W)4JG9@_cBQ68VoZBYl{okpguwQUbFs4rhPc5e(Bf1OFVo z-uAXgpJ~qZ^x0;G$H$p#Jw6_%#H)+ZK0e{`k>&>;&oRI8_+;}cuWAU! zsGm3R+Xp%+HS^8RDbqVr^fOQNqq;gJ$ZKtj`EG1Z4Rm!zpsO=|DT~n_p_qz`k)!8( z++;5GI4g4}e8d^w<_1r1HJ|f%k$KSL4#{sm#-`J(!Cy`LsX^LL>o@HTPsg-BgI}4X zeX)4~X`d-+KVQ;5S8$i4y+LrEyU$F!-~w7jV=fZhZD$TB&tg#3#b#-spNr%iMEb<$ zqrT;g&BX@hKwNA-=0|BU`ZGy=xilzC%gpIM{BqOk@#Ut=<0}xeF15|_!xdyA}yun6K4JZr-v&=51RHBq6ba;RiXz?`%2LR`NVkFx!7Fi|A~5=mqT7cAe_G0$>in91 z`zriGo{l;venND9zv&$4bbg=cTy5-EgL-$ryvP4y^R+;?4+Qn@!5}ZbDffwJ_($w$ zjPHKO_LPZxksIn_^Qif($4|)orM|nyeBaZbGQaZpX|vwr@0vF~{+{`>$KN-RO>Yd# zHN@i|8tw_Di_MR1Z=Lu#!!yveE`LpQ`yg1RCKHM?FA{%D$k4jBQ7>S5A^&}V8?z1OXx)>JxD;~clb@gpY`_KBVzmM7QOs`-Vj8gL_bM_9-hW}C0t7&{Q zC`*5`Z&KzBK^or)%F;%2hL`8Pz;16cOFTV_F86pqbe+coBUf5uvtyLBQ-h<|e3+f0 zw>;iC`isXyBOZBA7n@xpm#4AWJ*xEdJ)$~bt!rCuloo*ezu;~CLfkK;&3z|=#1^qQy7jNbJ4#OQAxpA+ z=uD4Ki@2qjF2?x1#p5$0p46oDv!Y8qJ}0`y<8!0cz}3jd9&@1F`7uNAL2~EE9Ki?6 zoga;Y50N`Rx&)WVogW_+JR;%_?kfb3jCcymrv)Dx@pyvU1eZoH0e?a8VbP1gl&u%P z9SGpo;zxPtSPt(7{szLA;?|%ULtLG0l5+S2=&LYCF;qe`?e1nF;`4&C*A|57jAr`q zIebK@E;jk7;Bj|!p~p)i^zFsP=7Q*9k1vd#^!VcFC66x&>hiMaK%c+Mqr*JDJh+$T z%7|xc(#7Vg=y;D;MrQ*50BP*Ooi6OptIk{cy%&e!>8Q5`;1}?2sktFqy|dHXHKMoQ zg1*XJD>~7-e4W%^t;^R(?6c9ucvlheZGk@jEvU<@g1UT1P?uK+b@|SqF5eX;eUiJQ zl{SLd+!NjG@fQN!ekuB)r{5dB;_;WG*FC;3dfVeWqv2L+^Z}xg9)CSbd;DNj#nf9R!1XBPSHGtqb}b!=V^bpE^ONKgNLM4b#qC%4Bu zBKp_7_>Smb^P)G<|D%Ea9}D#Vc)$85e>&=a7Vut(tJJ(1jo;Pj{||xw-w^$eK$unL zzk_=5$Dm#~3(hqDCE8KA6aQ82+2UhtHb!GS{qIri@q1A+wlyh^+38}FN^yKa7vpRL zH%f@j4yn794Em0#=RMvjwbtVyDUMI+VvJ7r@w(k5HOXU>Vw+AEn_;PAJ>Dbb>_%+% zO3`j;n7vc9rkXE*lDyD-c}wy_^JRnNh1QF=B`?%Qy(4*{_2SRc7LaEzeoj|wft|Hx zzH}h236d`drf%I$fYc@^es%c9L)O;^)Gz1$H)# zaSMkFCUMjIC^Ha%K>9o`z ze3&y*$++^YRMLi?ozj&G>iOK%WM5VrQzv+wN##7Am0IF)bLvu$TT<6}d|rzCO6X#< zD0LgKmiZ~7f6a@dQvA+a%m2}-4ZvEqr}AV1+b&N_;VbA(S{J6L*cZ||G9$&l5c&7w z7nVuJMaO(x4csL8*p+%_cL@r4x`I6F4)SP8kdI4KC1e5@n~Q?7_tDf$C4+uR>ST{E zP3h_f!!J*@dHUt4g2z{+uJ!oJ)NR1skPYj${g%nwZFr`I_)>%~3D)A9KwGp&vewWB z{0W31otSmwgSys0JuE}|FFQc@3q1x*_u*QD>fwL;TElh7-%_);lyjyi26isStf;dI zTx;0JdSD!F4)u6AX5!Vw<{;Via~F7Wj~2h`p-(S`G4!{KJCeIqub%xsUx26K|*%_*Kf!JOrBnbdhc z#@GYU?&*~#X*;USHJ)B$Zt}QRY|FEf_CpMkzUf85BZ}sKT;E_V-V5#0op^p3;Y%^2 zdh(v4GROdebzIAEI*iQ}gXJ-Cu{kQ3yFOa(CZj!~M!ZTIOSQ)X`71yK-n(PY?3(46__T zRGt(2m4{*ID9@GneM;nMG%rJ*yMb3>g^cCx0l^;$%9|@mjCVmGTT@{3vt}ILrNbPf zsZ6b}ALBm`gl&dT3?lXq@xS8kQ zT@kF&uY^|AVGLqAJpN>`j`k^^-WaX>Cw-V}Wi4CpabGO?tMzD!)TsebaF4k_>eQ}+ zm&(2Ge7zUHuuR@SA{^6t3x4|{d?{A%2Jh?id3~VI8v=cPhOdc>(c5r5xyHJSr{8XO z{80~g__0ci9aOV?n7ab~+#TrW9?{Q4k^jaZjW_kX7Sm#RGA+cDB`x=w9MYn5hF_Gl z%mICs`BIRUd+nQ)^D9AGz8a+E8^OBygF#>JKZ3s8!{#K*DK_6S=Xm@`u%7-MshjlT z+Gx^GdmIv}i?K7~YxGAM?!g){=Vvs3ztV5o8J>=5e+a*`B<*X=sNtq}p``aoNjt;D zva^AR_`5-Re;DjJc^11`)WzmUK{@}itWm0Md8%LAGT**SdCtTyFY^4%EQ37sip|gX zM_g=v5y(KlOB4CS!gEnfZ+a+n3h_mx(RfAh^7!Pwv|HwRn&3(lPD7 zAwcK46uqBa4=}wu1FteOgS5NeHszchj7^RY#wI5qXVk@*4Jr5dc>l#ijF}PYnJz{P zbe2Nk(}JA_yLGq4*@U5pm!Mvu>ie2#vuxy$1Qlk6?Y1mm41^OO&>DA?cCZeI5E z4%zEig0%Cy7|wgA1@`{u?4`U)_IJ&dG-_W|)2@9{Cao90 zu>5?ML^!7R4*brR^j>2=@6-F~AiZu@i*jD?`NYW4v19_r{%2BVZVCEnx61xTmF?Po z`zkY(jmR?#xKrd=ZTEA;1OFIhwFl!Y zwmUBgzE5oC?*xBUZ04T?-!C@vJ;7fyTr1q^XukV^;acHdg1>IKR(OcuZy4^I8YB2Y z^Ad2S;D-$NP0bYiALe=BIf5TH&jOz%_?zY#;AX+!GEV{L1%KN-0o)__5uAX9Jz9dl zW4;BvN^q}v2$-qw#cu}!xV88(y>zAc)d1g%@TKP9C_55Iw*zBNPxeu09oId9PYV42 z**l?i{J@Ak8m;38MJ2#==+D@aO*}F>7_{f{ZVz~}4f?g<17yDe!&8P5{N4b))QpKP zhYY(+r3|A*26%zbn1JWlV9(6BXmg`(cSEltL)#{=IsRq4bZ*Eak9+=9wMwm|_uI~b` zLhWRKk}mcmUgD8KyoUz!qKBEoH8{c?zS)@fg{kB}#yscwj}rbm*E%}jGsbdZT;qed zCIn$7`kgP>4;IV;rh_&83Q6Oeh?nP+u#F?*Ui@|-fLn_n(@y8~@b{_2HD;f~oF1wI zIjaL1YUDls7elj-C)c}&S|6@=bMG7-*Sq&q2z+!P=hQ&X+V?HzzSAgYY|ijq}lv@>y&2h4RaU}TkfDiXSs1ECk#-wg;WG%S6l{D&* zc&5lU$6W7aJ0Z~Fi6WcIc~bDr%N)F^F2+jFt{%?~xS*{`n|e>L!0NO*e&d|X%S{gE4UP=H z^P1x4=on7{zt4!wQ%wOf-wV77wlry{rYdv%&$W9yYP6s$ka z#LS{P#!HzWl6YsECw#oKf_U8yEyjCn5btrp8u9VL{Ot+$v=xRwF<9$7$?xf6yr<&# zti(Il?0xIwhrpog<-EyG}X9RrC4EUV28J``e zt1aa*{AAFC-yHB965zcN#?e2>_LF*82EBX%730HoB`5;!U#Wk7Ao8~q`=(g_#{#c{ zm7y)tIfs3vpEF75!=;~7FL*z(38xC)U*=#J2tGhoqH=-{G$r8kF~J9kjs2A1g9F=j z2tuf1pFHXB9cnoD@;QX>!B_z2KySKFaEZ)o@r_>mT==!Xd*Vk&TS5FV!n3cQLAsun zct;1eF6^I=4eZ7^ABKEr59nwI+QH{1;6t1P9)V7`!yjb(Ks_vjPP+ENzD$ep_<`7$ z&UCKke{elWdnsxK%f_w1t5AYjkM0({hm_S`!FvYd1)VSA`|P*uKmf-vvMbv>XC@hc z?H{ziCBb^oh@kx~@$G($5!!rZ37)0WSNR@f=s{eZi~fn=ePq10UhuyCu7SFKPAT|( z#PVeMe>SKW#2diJ(Lc!bf_h-R*lky>7mrz;eGtCa_&d_hy1?=h zg5b2^m4Yh-e@<|v;BN`868savS}WEI);Yt}F_fWJ!t5t_lHjypou!>E_(-8Q2%aK% zvEZWxe?l+@c5tf&<3HQGRt3G4yq~ zEx`13GW2(D-upQ#?>_J>MX!{0!`Z-OyzhFhZ1?vHY~9|{m)7r?_K@$GC{HPVEQyZ` z&jZZi;Q1o(D(vne&)*4FoB6ij{iMG!@K}b~U;6C`hPbrMD=zW&ko|BsFUtOdlaBKw zTFUrt_&wK{V1NGDV1NF2lOc0B&QP=}1TG8q?Wcn?a4LemtX08zN@eB?{@vQ(8-z*z z`vUG6`?U{KCug|vk@MAWdiqhpc}i2wut+^P*InxI3_0&e-^G{Npzi^_@!Jjdmr4X5 zB>kl^f)DPuzvR9_W`0$JPa5G%vCHJ_(WbW^*zFmcEBF}6_j3i$lKj$qoGtmK`FO01 zvos%%ld_?59SA+OU~~RGD3kXD z^8;T9=3~F)`)0AZFZc%GfnYxN8$sE7IIt(*k?(yNb8LEX+MJW&@!%WMCxShbPnlbN zn5ToY@4gp&Ble7W#E1DIxT@nGuqQqKvETI{o9E4Ip8kUW&WmTG?di+UOW4Wc`25n0 z^LVY_%@Ld5%D#Grip}r+`4};Fm?h=^HFJr7_jU7WkN?{|;PG4LKRn)GzT@#b=Eokt zYkuqT-*B>slWmh3>amIT@HiD6=JCKNDf2r9=TPk&oI|xsl=I<-MOS;gSM({5_lZ_{ zJUn{P;{&7r@c7{9`yP*se(3R-=mn4OGH-dDj{fd(MKoZ5F~3Dw?J>8Bo&S^Ie-k^e z_2_oB^DurrDBW+NE!b7?9kNI30KuzekCwLApEJ0f+18^wWsep$*Nfi{1aNEdW4v^% zM}vWn03WQ|MkNzXZx!$=Qxzrs1a}${^Sv%|e7MginJbtUm4Fu=)(fIT@mJ-ql|3+1 zMb1h6?m;?C-c)%UZUU_y^0>LLS-{7E59!1olh#6q8z$j2yyX#4?CIxeLug~f??cT6Z@zO$pntyI9#j{_~4*_ zcnE4O)`n)VZtf-Z_cF-9Ij;WpOE5fLDSoR3X?cDPX}KEjvTVN&{9gh7AeVFMf#v+L zc^|F;u@2JxE3E;owfVcf?m_q+_?MzyvrbWWtmDr8qq9xM*g&kOv=#3NpF_;>GSj=` z@w7PyNk3E13OG3EXNK!a;n?=jpl?|kjCT$XzI!@C_A)W*7+(v(tcb%t;b2~UT+rv7 z7@Y4~WDSKS@1m+w{iM421#s}Y%xGcQ5rdXf1Uk$H~bSvpn~JX^-9 zt%8pY<`$4q)^Tru%ME_luQN|f<6JYA|F8KJOvAmOcEqI6R#pm&S z55kv1QUg_fCY~)MiJ9mwXy;pVo#{Y{RFO=`E`N*>izU}Fk`7ftqe3}0Y=#Po6J|gYx zGlCmMhd&j(KycjQ|ac4V9UE0y|6B^ZVdPZc%Z*=RON+S$66R9TPT(g;6u^0yXIH`Nq?SC&t3|q6=&~YRQ(arf z|DkXVx>>xFf_Ek%-n#0ls){NES9SXgEH7igTHy^`H@-Ywt2yaeCSg%9eAsd_O;>?s zb6aPlEjYF~z^@X%rm3@gL0eYKlr0PJs->4`EA+P+5C8Hs1F%@OWiRkbC`abzYjGWK z+hi%5Y?*7kS#shG)(X8Y|{#2^*bKyx2n#zwJw;5UuHtfRaY(mVympY6px+tt<4xxJV#Y%FBA zo8-8vxwWtzx#tVpOD(h?jcwbDdtqr#l-}K7jZV3=My&AVqfK{%7(3J}kYQ>-o#qicVF9sfw2^D08(3En6xPI2dBL@#!cZu6F|U|*RM-bg4a349C>7Dy zx}Y!tAi@HORGv&L3px{l(B;)GI0&TI+VFvgPaeWoq z)-pB~`7@P#v0^lDz;;oknM%uVVQZ5oGmVxUP?Q;N{}?85wop6-$g4Ri-= zf~1C;uF(7>mm;#}Ci1M%+U}EDp*ac8&AHCJk5S7libSD-x2tP)sL+B7skf$I*>5|L zFEr93@z3N_MwOSdiFk>)|pw9SC3kF)Yd{wO5Vs!QC1MWqRPe4UQDlbQDlnfmE{t_)^{GZ*g)sI zao_xQY(>%fXBykKTYF*IH+C)DZjC8314WmA7NhTp(px2^x3;jjA)n2#^XZB%DBTIk z^+)+;K8KL*9=yl$h3bM^n>8q7#%IQHd5!nl`TTiWL*Swr!aXtTc|-wrJNJFE?7Yf47l@j%#DSeH(Mkwlz0wA6d7_$hEIp zbFEuDjPJ{_(3oE&W~7kMvxZEPHXCMpzg#O!@vf}xulykFtp zPs4)2BhzI`c-{70v#mJ|MUr+}v(0Ud3vG{;R5dr*#tVGF3N{z%x@;yOY5z6b6p*y* znq82PY_ApR@VqK3#hPUo1SBYn?DMTYR8_fjkF!ky2|AHb8VLDTRUl6WW2_pl||*DI`M$I5VR=>T2rL|nj|Q6h@j0$(3-NMf>P^) z)M{TV)KnBDYf=&vI!Dm9rb58Cx+r1gNy)51b|7@2tI=v2tkGzb*4o(~61AYy zrn8#BqEC`(6{#&~Us#D6?3fKS_C71I_>j$LHrX`C(ayNP+LexA}P7RzHvUIhWOiD<# zBY7cX-nz(3hw}^a`G&qeI-m?2hTX-|pTVM;L!>sVN5 zaY<2wARa-471}(8C5>8gT4awNl3*$pl6Jeo#TjGUp@)U)a%oZ(qT`NI?qap`NnKb6 zwxoYp!Za2ayUai%diLOv37x1UY$CK)b10dv49c=Zzo0Rnm72tSED|E|X=z>Pw4%bO zqJlSU6&L+A1i@`?y5TEw!{#IO=jHR#qYhGe=*(@q@yEm*G$5Tv=5QmH9H|bpz>*`H zvBB7?BiFbvYa0-6=kiz}&tmr6H(#oSj_d^>ffqD8MtKGUD<55fUXnD%%3-HwNt%Pz ze7dZ;D~rO9FsSUp#?3&XA8YB>-fGv@(QK{i(jG7drJ4zg7g!30i&p(?s~D+BBK4ec zLz0C}oWVuZtlqUU8fYkMC{OxT($A2BP(;?QhS0Ifk*-k1plt=KqLvk%743&f*mPw> zh3z&YEc#*Ej8X~_jg#3TjEW}BQJRC~e6^pz^*w!`b?i^LY1IOg#LWy~Bg-`Si&dqq znGq^(lrx>fl(UqC!hjT1^D+%^tjIP zp;cvW33sHTLK`kdvJDf_j!E3kZMF7ndlgd2ww<0Aqr5`Tn4iEx0ThA0Gztgw%9V~> z!P3~O3oYN!wgi*;%n)?XShdqhiIFw{itS(s2GIJ-fVN^aH5MAh2h|i+u#z=X|DdPI zo(}UwV}LA`GKnM+p&+y@^bvC!DP4(2Y$Nc~Gh$`8Y*yT&1WT~SV{JjD&NWF)wpyz( zpogo~J~=YUgs^DVymc*DxkOxPl<2<=%TQ~RtN}IPFSTAHTCc^DT0UFYf(^ksSvz`d zeJX^u3%1g*%2wA|4lpauRM3XV=!luX=#Zuwtrv_HSpcHgiW(W!{3BaI?b#I=;U%V` zMzfo5Z$ky;TDNTtYgD__C|1YLfKFnalC*EScr5|!q-#X2Eaj~`NwONgAmLr=)dZFKex3I3Bh{1s! zy-;`5%ZtIrs>o+vi)Y9RM?Vz<3V!4q31Ar{*np9(wpn+iWUWdq> zT!t4r+znd05t4>U=g6`$ktw}>OSe;G4U4h=5o4gqj0|Pf8nP_9#u(}e6?VY(_OlJS z7T5`)qO;`Y#JHg(pxSA%20Lg`zHVBqA={!2go?qbF&G~*MubH^I*mgg8HU;LI){@* zlzqeNOb&g*@H&e_D#nvCI8#PgzF@?4HbVo&vkV~&cU@pJV;{wDu4824mX$0XThcOH zoh_)DCK97;%&>%N<{CH@D%3>Hpi*-}P1H=rc9?|xOY}@hIF+~sMT?g<0HLOZtj52u zs>KTyO_(LR7P6hSEmpSppo{FZjbc8>7jy1u5pAQ2w)1* z8#Zh?4t~x);4n27N%9OE61w>8dL%N_6f}( zTO2U+C!KC3+S0%s7?|gxa*EN8?6Y|qB#;_p84ff!7gO-kSEcGX)lQHhd-_vOysnMzJi6m zyt;@z5-m+nASjcpiU>G4No}F2WT8n{zJFFlqH=WKN2&2rZrNZmY$H<1Y_G*mNA6|^ z-D$K5Ym-8#g!a2DvAECa00aO}DYQ+@(xO-DLdBzqC2_G!$2g&XC?<)FDiF0Ku_0@^ zA&!g9=`D%PzfWT_jR96Nptp2N{ z(xw9*zD_tMlm}dbf_tKG3977;tElAibca;^qyZH@c3_%0pGk-+QAd%~TK8Qs3HaNZ zQN^0khrbuJC<3u>(tZXpkm#c5$QBmlG0EH3oayXB_ok~eBL?RbQJ7?4RdpuUn)HEn z2%v?69{8!AC7GgVwOhV%Hx6P$SN446ik!(;UYPB&RgF4L*I~20(;$phs8AF)YR>s$i*!|8kRhV) zWXVu70jt_N9qu9)iC}Lj5!REqcHdR#&P|@FQ=XjA&yAAomQ@3c``g8=kSLnNQVQn2 z!*z1(+13eYjCY9BA|W3*-J0S5y4}ph(cU)-wA#9eGE?VAjeIbbM9EbQBdaVqUD7Z) zloz{XVWbH{-9ngLSAMZeik%(JX~OVAoAc2z2H3L|74a5gdeN?fSRn%m3|11c%T2{G zZPLAUFSisu$&wBNvN{Ogj_i`Y32WzY?^F$9hk1|#Ss|RPOu6$qQGBf^$WxYNDmT}L zU3*SROt;gANGMrkaw>&=d9i9Fh(bhXd^B?MfV~bT*TlsX8&9qfKF}g@4ip7~6Xwu2 z38)f6!1NYp&aIZY0bfLI)x}2y$3{kZLzHcYcdGeZQ>zvn=3IeAZzj_Z*_zv&7F}e9 zbN5aosyCMqnTzB!>*DT?AP^+T%5pViSS*VqRwX)YvANXT)}3!Dv@P&j%{MMa8dSjc zPJc=s80tbpvsJJ({8j~AX@D*vFLSh# z)msDe0hwXzn&5z;E>Sp!vI-(;)*zZtWC|A_8d~!WEYY$tqFDy9M!^R@IU6H! z$(wx5%Pu8LWpi(`pztkzaMU2HEs*%Uq{>J|(invwO^s89uoJ;m3JK7ZZQDG6sz=g- zgvooh_MywbsxE47YDEQYT24WDKCha(k9EuZyQ4tcmHprAY=V4w~_f*`b@FDMJ zI^f6rXzKI4fD08&r({;3E%seS0T}5cs_-fA`nBGR4EW%3#BK~hu3!#<^%!*#-XKd) zQ20ud%su3?IUHVz0(8UF1^5KT=3OV!@0w0|13JN2!%psKvia;s5X>m)DOl5o;sbM} z!syBhfAtq;}#FVuXl-U%ay z;?Bn6Q0{%%u~_szw-DM$WH%vtwh1FFGHu3Z@;qoztEr2$i;3luAHMXtg@6?)3gYPcCT45dk5RQbFZ)W7pNy^`D9Rc1BAcDj5EWXmSH&Bh z;-bOrzpesy(*mo+=EU2<|$Z82?Bk;jq|3>!cAQEi}pD;-FFEVC6slu?G5%oy- zAyG7}(b3{)IcX}EE_idR)(ewQ4uLWgxkm^_3YoMu8w(PqZYu|wW6Y%7kwrGe(xl;m zHYIDjk*JtBV?(qe{VK7>i!@M&s*cr(^$tS4fn!Mg(@tk+4p_u9jrbp5xWv>uOC_@z>Qlc;d`k@O8DM4euj8 z()p8pSsc3{My1k(I4Z|VLJhvImTzP%H4Mv9jD&?ru%A)u$1gSoAz9W(H4z}YkEoG0 zCR{4Ke2pD=pq+&W+DnATs)2kHj_{nw0U!Idbl}0MGf6taV@}P5*JNTCCHw;qO(`ow z(bv_8g0o^01-d4%(4H03%kN^LN%(}C(66iE1dAgQ5(Iisz)n=c}3 z?xHmC8%)ncvlg6_kE#))W-CfU@=-OYz7X3@^qkAWv_SCTGc@6*axp!xA3lqokqFNA zfJjBd7J!PmGMymg@_??Pkc^oZolF8*r=$=yW-B;@qAYZN2|-DLXOKCyq6>y# zK^~GFK7nP=7_-U$FQ4AB;}^UN#W8a9v(k0{{L@=(0K&`VeLlUVG~j^52A|#v(XA+` zIW8LdSrVpFxL?l|jWA!ViFK)LZg&$c($+zxwyop$+lZ94Lkx^@upYwy>3%zq-*j_UMA8Fuw4E!y0>0j=h5mdm=Ycrv%lQEbH`ai5P!s0|I?la^| z_>`m2h12BTx8sEjJZ+mt)8U%PUi{2FcU~g!K>X-N;Ky(&{GP(GboiN#|uHiaI5|u*FS;gobx*hgdiG| zy9ZZR;7C2^TsU6w7KZ!eJ2+9#1;Sq!j(?d}=UU#y6;x0XkE9=gALSc{AD=^hPrYZ% zX#v6UL$J*p^-bac8!Ppy&l)rJr_TXH5C-^Mp)a2YDX{+!Fc36w1I&)W6T`a$zLh)x zsR-p9Xx`j3BzS|*OzS6VOfSm=gWJW-mC2M+4$SBkLkU|KDXiT;C)SRi+#Qbe{*<#$UgHu=NKQ4*B`gP>HQ7< zlD|8ilVRx3O8|I{v6FulJ*=buUBJAWlbUDx+%c65LjU^-tmE@H-2?ve`H4Qy-|zGM z(>~8H;rULh&)pH>V=oAf2Yq5o(+ z^P0JjSpPCSzcLj3@R$BPDE^i~ruQ-HKLyV_r%dlN_IWy*8nF=WpVfS4;4Se1?A<&jX>qJFNd2Jbz_3)5~&6 z|7Y<0w~?mzB>d&`Pw|&m!pGL1wvtB?kF(Ez!1KQ&KUuGs|9`~u6L@CLG z53l%Gw_n0<7d)@t8U6^z_;?+{_+3pe^~C4lc;?kV2iy2c@ysja9Y0=^z^l@#t^X7} zUk>>f+vnr)oW*mAeV&77UV-cM--PF{LjO*m?RdTu&%4<0i}8$W5PCmmpD)Mr6UhHl z?eh(I<}vQ8?ek~x{7_K7SK*o04DlKprsvCez7_i3%ZBHb3_OxKYo8y*^E11f-rMc- zck#^YnNddM{pav}_70|Zs^#}zc>WgRqot(2e}(5|!%c6-`oDo^9-quZb{YS>c>V+O zf0}*Xgy+tII9}O?AB1wkYd>i>8GaW$^NRU8`+NwV=MONw``hs2@H}G(_}k|+p6}hm z^g@U7{&YOI;n~G^0-kSzyhGtndFSGJ6y!bAhBy9LRjluzABK-HIOkI4)_8Oc03N{- zi7YgP1X&T-Y)M23$x4Sl`;`iP^y?CsFheYycJIz{z7r4l-_AR7*3V7$`FSosJ*1sp zyr3Og0PRz9-gfi;7zEE5S+}rrM);FoY`6~Qr=4?6@?O{ZTy`*AY_t<#b}>$uEV;ta zrgNfx3e>7am%q4_Wh0SsNT0Br*z`-)h#S*)zQgCIeBDf~o8%M^VQj}3TylW)5Yf+e zOBg@#EIj261cYWhYd@RJbYsy*79(|0(MiqWmk*Eel+IDx2|2GW4Ak-YrV;r$Lkw*M zbGKXqmgQn9l@H)Kc`KHM%D)!bAzddLtpttC9Q3e_V&F*&4Q$PlF^ej0W& za#Ox#Gui}+YC@)&ZN`W0{~rykw$a|dn_f3Ea(`T(i%wY&u3>P_g;@dmG7wiTKwnJ4 zXo!UvRQ8~=LH9AiN`FT%d#LCO@4+zYmI-FhOP%A-r!C=I5uWkV{XW2acHwz7hzqkK zz+sr5_d~Bjr^>w>hQF#G`b(C+=gyItxObASQ->2DNfu_2V1edF!R*NxbALd81cS&P zlh;7_-=&2Fho46nC)-%$4e=r9#9a#9!+>z}&?$BL?-s1|Ukg_Kqwns+w+mMN-x$!p zeGtQNgk;Pw1S_8z=tTF}iwDmUtn|MNK1(0)zmxd@+~Ytw-$K&bfO~9+p9{{&)7t_4 zyb^|K7y2cF1)4!4NWV}X_7|-5xq^H20sp&rNpQFoVJO3J=;`6$-Qgq2ld~`CT=)r~ zGyIaN7>8QS5I?f`K@3br9?ExTl5DC4Co?Cjg{uEKe5`~~+Lr_$ArFrS^s`FIv%r0T zjDlmG3;%i0$>+;oA7I{+FqINwD3a7;F9|$CaIMg%37#zY9Kl*IE)aZ_(619bP4Jfm z&k+2S;CjI?3!W`F8pXJDQtKeWCkwq!@I1k%3!X2yAow)F*9qzwuTEV3#B0Z*6@MVHK1^-?!-&D8S0sM71jjGP8 z!3!9lP6u9v5!PV%&lkL>%ro*eUWMyijB9Imun)WvY-f)T^mAXlsutNJj#u5+d0Zj$ zTYQXh>B_mDexy0yT7VB7kc62Q;Kl%75#YN6Og$`v{P~v1JOcV- zHZ6k&Ciu(%KNR4-2PI*;0{m=%$02hZ&#Nr{pK^sB`w~tz_9YztSGrp4Gsxdk)KZrD zyMR~W5@XhPI$rzd9P2o;UtCznOF`j0)f;pxr*Ywwx)^QZ0*|Y3VH2*sN4!1YPy6tW;KTa8 zZjkeGbW96zg!PqDTv^?XwCn=B%G3lp(Kd_WV>2m`|Hwf8qk^lXjt;JHo*G=QH9fcz zY=)UZhH$Z|53X&V8Q8Ly<6^&3w=S+3t!{Y(n< zGr3>=ylUU2e%`?E0MXBUlfj(j5u%@Yq94`ODM4Oq+sfB@-F1@JX9T)B)0eUsmy;jH zz;L{#E3yAg=29hto(-?=tPr z;8!JSUu-5~e)2d;`}vag(*$=(+M5LDWzDNga6wwj9>LvmO*q-|dNAi=T%MNb=OVMK z^@zc!8=vle(Mest8s$MCxvX(=_IF!y>rTq%0cw6737Xxgt5J!slj`d&kfcb$vPbv~}x zTpy(UGyZbY*xVHK8E&>cYo_;>;M%`i<$65wjd3Yli;W;Q{}x~@d8V|%{j)9cp>WAi936IU0*;@si!8uNXRpEAGl_-V7=^k--YQV9)xDB)cnaj zfOQ^C;~yoxn#MPSvh*i}Ru`KMK^or)%F@Q*ip}=|yS>RQ@$W{_FIk!b--HJ-V*(2nc2{<&1ZNz z>WXV%dy1|Oh&JqqH9lbUk>uKM)zyA-z4v&b?;j15H(4GIuw00bh>|)uD#CP$I9yp8 zB{qC)l=K0{MI2|)q0bYwDg>SwCHgE6^qCHHSQ#aCuqsMyRCRQOl_)l~QH95oqvJfT zi@26d$7{=%dVEyW%^}-$eLQ(;VH3wNS0IO;Qe@27MLg9EM70 z>bco!llZ)#?6n19I-{9Bd@f4b%6x>ezqlAZ;0rxo5`ER<3!;ZTzA$>yh zmqc$jM1RE|0M*+yqPKOTw`)ZwT9>br`m1&M`pC_2#O4Oek@~zX(C5Deb$L}#m+uJb z^6H>2-x<{9yP}IlO7grrTIumU(aj!zA<*rYq91ztz0oTke>r;H=0$Iy|3?G;KNjf!@qYDB{&dv;Ea3eRSE+e3 zx*U69RR4bn^#6wF|8Rs^W&S&;7k>=ug|pyH<6oj3g*)+Iqk}!(7-0%kTx|Xx#U8&G zC3Afy#W6cwY*HzXFX&=3Amv60vDqPYw~|5MG4;I1JEhioJS4^ODP4>mRr`3|?vk41 zu}QH_r;E+7)Uh7#k#cqebDJsJ4Gpt*iq=%~1IWYAW_V9oNIS;fg0r4RzSEpiAl1gm;h}0eo zE9K{)6z4toZZCc={90gV(-;@=9Eod8YR6%&3>=Z-{EynhQNmyC;pkM-w;2=2Gd_@K zLh2B_OBb86)F_3(<*9m)D^e$VT$x(vadj%^aZQRNV{I$P_L~+rdS+TK!tWeO%aN&N zJ}r}iv`h}tQWvD<$RI8CL0V>}l78B(RMJm7HZ{e|d0gr=kB?6!^A#ti65Bo}mDu*V zsk?mmd8x#n%ugj_rPES>@L|qKCF9DoQb`+ncB|BglQD+ml){xA39)OiYbqs@P4so$L$Q%vyMeyuF z==b`uEz@lHQv4i)7BN(j;w1mAS@;ufYtcI*i}Nw){lWeuzQR zH~m8Jh@$x)C;wW!7TTpd@%*ajq0Za`J>U^CxD{FABZIt{Vs>KyxY!(p98ecS`-gZu zEtrd*j#V0U)Oi_xe?we-ZQ#!kud{(K0S}(6Hjwy(Yy+tW+Q2cN_@8G3FR4F%X5=Pm zA!7JA6zAJJ2Iop5cG2CFAiZiary}mxC=u1zoh*WH-E(|%U>VZ zyr%v=^ph{W_`TbATbx=ydbbDXzjw#=yBqW5z?9kT!TR?G4C2$(bD~wB#@{PbSJ%JS zyveNp{?m^OgTX872VYsgX}1UW{sg>NJp1U{RP^kM#~%I7z}+4k^hv%saM+L5Hm|7P zh&VPPj(wZg3}R43x<&$E7u)%$W<3{g8q&RA^Ct21UTf;#WzhBM-5xw);$-*qx>9_5pRn3~OcnQo_n3Fh-^{s(t% zK>t>N>F2`iH)zZ6F7Ai^d_VLuG;~g$kpCwwo%??R*!D@xa3f@oX zQw5{r1b3?70|mDUK1lE~!8$U(NpOkK?-e{k@RNc^3jT%QLj}JhxK!{iC~!S?h2wC+ zhYNj*;3EW|Dp*HNIl*IvewE;HfPRf3lbMrDD!S+KU^_X(~Q`WnG#tl-uPhB1cwvtX@ByP(nPF;j$I zDww?xVZ&4Df~k)7!~eyX%(Y3d39MC1)I_Yz+Y+@tKxX)rfV|4Rj{$18$W|AS$MT=??^tNw2d z=#RoYxxK`{7OZ@ZfvIwPi5mqgJwnH`$M>X@d^`kn%E`UNISHfTetlh94sj#|l>ZrGgRII>+Zm(8_y|dN~qrLC4Nw_?j_#0oOE4zZ4|6~iJuUxdx^`^46{Ru zd~O%4dx?J|Soac-LJ@I$iLVr_dx_Tz=9}tVe(}lSYE*TO_t`I{+ZN;f+2HwqGv1#k zGL)FFLx%H!|2L2K^OANQ?_VVN{g3xQE_$dlXYB6sqE3P2%eEcw-;B8Wj`vp}-oc2g z2|o_3xYyL-xu9t+HKq}`2S2(G!Z?L`pnY@B=_J(Ey&vjI)I&XNNmu5W88arAj5_AT z`J+oJCcrTLvuAsj?>0Ho2VW~rGz&ACH11?<&*2K6rU^V}4(EmQV4Epj@~3Wmy1cTc zvbLhSvbN5{nmQv#JuxWG9%9h+=(@7H@-%$fIy)AYI7T=;iM|!1`(@PK!4sLXP5u=e zbH-OH!7y}qs;KeKMzh(=I1G(1SG}K?iv-1aX;X3eOda2@#@pp#+#c7Jc?m*br4fdk ziG(xPJm;Ftmi0Y|mt+>wH zKI>U@$%}zl$lkdX8n0s;MvXk0TUiGM2hnaOA2MNXRb5)r)zGk@yA{`sci>_Z+!KUT z&?Je_`54HCBfJ`>cjA^*sUTLri#u=mqFR!} zU!^r-g~!2b!Qrpc@_>xfQRP@u)HONp&YqFkt!D`eQ4h{6KKzw!1cQ>A9H&X!Z>hoI zuRIRiQNytQ+)T7~_V8EA!=SJLB9$j^`S4ds6~p5!x}w8h;Q>pk$pd$H#w*CMq9BVd ziaq@Cpdh<5UJ+1}+4t~QcxXyDJNy+snp#8=9{vhC)5`;s`yT!Zk{pPNzc}MGlbrGj zJ@D{XJwVu5#^4lK_%oH{8J+?QGE+%1`1w;{L1r2)*&Ub)DwF7_?i5&%nLtbC;iP&r zD(Fm}r3a_L!o#HxnHQV_3y&am{uEerYHZpdqdx@}R5X#o9$HFHfd$>B6rqDtU_p0D z4NrjuIY??)=5Qu6^PgOb$eNqTv*Z+5FwvYWIt3O~Ew@Fdz=Em;7gBE@t5Il_YnAZ_ z7ti2WQ>ZCCVHR4(sjm=24wKarWV8=zEx6s|DVqHt>S-s8nIM16gn5-bm77%Ui6Ig;15d}?)B6QQ%TkGWse?4 z05m!7m}Cr>LHH+wZ48ShP12*3X^wHb9Bvoe4jN;!aCt+XTH`G**|sJ$7HMrPYRhmN z2_KL=Cp`qicns~KoKBvGTrQC~H@hfUrIR0l#^G-2x(;PehG=TXXe{HZ%j7U}-kqlY z(5v1PAW}do$fz=*#l?laddHne1uatod9R@vLqfia7Fc-WI!W7vs6TF`vi`0C=^$sm zg63IXLEGo=%$4)inaO&)0REu)#+6M}mQ@jYBbXu(6)x0snb{CI<3c*d9MU+k4U$Hc zL?qxU`ZzO7Lc-dZjfK%J0 zbI2HQ>p}3g-d9M8%I%Hz`JAN%T~(a!?%zi@HF&?md!%8(;F0OFB)o175AW8I*GbY_ zy0iBBY#k0cbCYevopqMP`^?cd$z}qQ-jI-O3P{>@%`Qktw$}kf``<@KB#v%w2ZYQC}~+lKSDpS9!&6Qo z1xC9dBC8k&(&z4!#Yhi#pF^g)Ok1NX8A_C^WpU$!%>X35yj*L7?E_5Wn^K4Q=m=(X(@L z2Vm%>!}$gIJnlCu_R$f&+~gL%Va;OQW+{@HB3xd_n!r2ka3Kdy4{gA$4GTKkP$#&e zA&F*vD6i&y400Kb2rl&jC*TGoO?V)yCOWw%H%M{a=5aZ3Sfd~gW^?4yJ5<8f&JLRi zp;TJOI=qTiqC~=e#L2gG(4rT~(qBYQB+fcPBoyJKRM;E9K}G@3SEa)uAfn(XXiUy7 zLCMj`R31{hb2(2{9kI;0t2si^GVQn!w*$BHh`6>`pu?KSVrDlMF)dxogwO-^+bN!I z-1#QG?e6kwm%&1k!pr(Q^lx2yYxA90#Vm`X0ix7!VOY1_X6%|Gm6}(}qxahAT2yS!J z4PR+!H6NirFQ1nlbx@?or0v#>NQ=#A4jO=|!)OmVESEl`zVkjF)_2WVG$L+nWp8iyHL)m`yl`6C zxXIIQUZ5;=Yj5q9(w1gxRTnP3hgcYtY9=gRU@1tFRX^J*mIIMQ>N(?vBnxrVrAWf+ zT`Qx3hO&n8q+ccd3@HdjWbJAQ9ar3?E3~Oc+X`uMX?xPLq93IFFbSKkY^bo^W`spQ zOq)?kA);|I8yv8XA)-lhl;$c-zFLbd)en{;mCrgXETB|rr7SQ>UCK?CX}}JvO53h9 z$*v8SCb=LPx~tHVlvWcK3Yfv7v6Lf7tO}D`qlM&DwYaq@tJk$-;>wQInOnjgI;qfx zi;--@MA*9pw#~@g_9~>3Z96^ldT!KOTzikJ=%5JnrBOJL@x1SjSMCapt-8?i_Ksy_ z2)bvi+UcakNE-l~-GMbQRH#Z@Um4I=EWHFuZX9J5^``A2HA z-pVWya~dgKiCDtv8L_fkHYsn|~ zxkOxPl<2<=%MfphU_HQJnvfdNdad3~yagMAbuwOg)u%#eyI?B~swxIGmIKU+GZnNU zGCE?#V01_m@5x0LFjkd8Y(OtPDAZ$ky)GF>QW+tmnM(?NaX zdRwU5&VW>N8Wrl}5J<{dSGL(-!fea7TyZV!Ls6?GgF}>catc~4u!nkOc&L8cBn8|( zf({rGP>;E+>AbPJFp3QWGLgBD?g~7xGCqyt1My=1(hKdBdY#nVtUhc?i+tuS>FjD! zKV~HTkdyk>Y2J%I<Kr+`sbPp zPBof-WJ7(6%Ldt6z?{+nKo%8Hsxe8>z-B{aPACVR)UzAr<3E8JsC2EMG9< zI-8+^;#r0ehPy7XnX!-JH`g(Ca%@@2;;|(yv(?#xnrR}TX3VfK%}$|Q1BXI|ny496 zYEGz$n#tG>lZy0ANjR0b1x1UOHUOcfg{;QEud2lh7EPEXx)!pXwJlb*_@ImIw2fju z#}{+C!I;`c6~o*|E-AZ^SuAU=0jj_aO|H;ESad0J*fS_o8hqDv7IIk235eP)&tVY? zcadD^3%FjDn`^*0D4^=-2!XW^4YtfzE%q3`w8@R9O7(LdY72tI&=gQ0RD0~p2ol4b zv8hQSknFOl3DStXg?f>-VH$1kL#Rn2lOj`;N|--aXu@QYY)eg;A|a!ifVb2JUi4v%0%~z1U7Lt4gHBbWzfi(u8do=_Z3$oXP#^^G`D?-RRyK19MG!bMv{6baG zhl>nhaah`J`v%rFa@Q6H@wms?s{$HS0Sc&@ZQU3^YgX9e;O?4MqAd-)1-lueFc7sb zRLiTRI}0tGz)~XCnlM-e)y}Me%0`X1YbVBgo|8}ZjUfp4iE+v$kT1!q;#eWy=COiB zpGeibF;Xy15{T@xc^V{;8e|y`G&ncQeaw{1nh0)9!#?aV%bfSny zTQPC7H3wbqn^PCdo=kf|8bADTr9P3<7JBY;Lrxd5N1~0LM61{U5Wh>7=JJTcuJvd zVwM)YQWq*7-N7U-cIg-w^bf@(kx>PrmLxW0O*h1Gu{pgZvH5rEy!NJbUr>>r&1#`Z zv-wcTe5h?qtc*)rV*~F;mbAK}0v6Sk0Sl}DDyg*TfQPRWjtS)fm!ROD=v#s+tK=#w zxjfw=RX=G!MUNerX3l34qDs_JB(>IkS4;x_wq{hZX7u6j1ucp|?3=WoK@23iC_1u* z1$j*Jwl!xuyU@Mq>dc72IYksE8CX@F$+aeZU>yQzp`ZtTYJ9DSCWt=V!L1WWNsN}B z!A4mfZ$lRCgjk=Pt9Vr?8bKGnqE1FF2rk`qbmHv`yarOU7gmq`K=?a93eaWkEPUDV z3w$$;g$(A7Su)_s^2X8NTk^J&jrZ`p`|?gV)`Kpv#{aCQn1x?S=2G z_37&RjdRwG#TUeUe0NhfL$vgyajfBBd@l>)dAJy&u%)7fiV&=3w#8>asSFf&r z7st-L`04fUK}yJub7wwRR)s4u>^U@R>Nhf_Ag?ql>fc>{@w=vQ2pFxLKXj$3OLe8z z?~W&&Lqposp$M}6=gTkNXbSr>1jwLGAG&fr{0Y|YDP%Gu6(S8?k+N|Ow{hLU_UH#zvpjST|K6lNR=yqjX^WR-Lcj$^aL)Y)3JoI_kc@cdm-@r%L4jqu1 zBaed>Z>nN+>v%;lG^ai*rVoU=Hg&%%Y?k*9oU`srDe=F&w|n2dUeDo(8@`GNprQ@y z59p(iSF3Oi&zgE3KDcRR{f0iNg=P=iv>U#q?=1|qfnHsW1ZoPV4s=Sd6aG-ZlJYP$ zD~DXYR|G9GG4+EdJ+`F``Htk;Y|r56Eof}lP-AnD{DGUgS$d&_bwAg%LpsQ1*6+Ak zT6kK~@>)}9vc&{B0g~i>O0s^~iquLRG&CPd+H3T)kFFcKV%k9Q7`|fqVA1DBM7NIT zDMOP`^6m+-@OM$KJF*C|^GHW%(w##z9LcY%ABKW5i0`w~?Sk0&-mv`Q4N{3EwZ0N>kV?EkD)9zeiO-Z; zHFV|F!FAIIcONfVhHPauas{t$Sa+r5Eo(Tu223A_%$4jLnkfG(f3tOD_oaOHxX;WCbyZ2+6?jGVy%KDwW$q`XDB%+k6 zC?Z5sh**R-NyO@Mwn_f9LS_T`B!W>kUfx1YHt0;^)MV8&2E?d2y7jb-9G*}1!FS+lZ$3x zqcyqCfao*Qv{BSaI}ob#SS{%49hN6ir!`}NI`_BX*?FMbi_VNLz>v^TFVr4@XA@Ofra_lxUzI==*h zf!KiP4yO+)?ThP9>*M3!p9Bx20mq5-ZhCaxJ}c_i$8#`kj3e zFhDd>qec`77zE@YC~rgp?1QL)@e%P+R1{(ad_f|hMh`}maGKT^6<=*Y>!YHzKB)x^ zXaOysVryHhs1d2|c%qGhf|{E9|F79IdlC{qZ@=ID-Fs_CclhtwYtLiNnl)=4XRrE= z@f~cwWIT|LSdW%iSROP%2W0*91xQ0OzGGxO^SX9*kIPL^m9t3G?@Fd;SJZk`Z2UOA zlo6>kd)`Cgd$)xF(~)8~B;ySz_HZe78^l#Dh;2=-Xr%y>L1nyFmolrW5@lOhUeDrR zA4^@IjMv-pG??<1p6%9o3up>@@C6hsttq}GS+u2+&0Swm8Q+{N+FV(*siJ(7TCmT; z_$HDizh>FEEy}vI#@wv8{_q4T2(y2 zqx;#Z%4_kt53(WFR8||@jfY^6R?5Y@MPw@D+sL6z&2Ekr+bTAtRi<}@sxG0CHIsu5 zTYp<{j=Y~!7<_@x0wJNg;m8BcsM5iQ4xuQe2HO03oBSmItpPq_#MjV0*3}-2aem$Q z%nqp1o5}c_rkhukXR5LiZ7Zz{HT%NyJ>{_aO^Yp6Qic)Ov1tttJ&^_17>>_930(ok)={8D6qehrBG$jFru3 zJ)iGWE0Yy?tgV#397r*gB0GFMF^#tNG;JLRnW(2kXX1Ne>LpqL)-3BKS|BGl8lU`- zo)T1`6S4;U7<5zJL8gZ_l?A~pt(B#}Hac1$2u^G9#J>M$5NDsltF80 z|5&G81=Cg4O-AiQu?>Pb`bn@JI?eXs9ifD37G?(@!R~2|g`Hg%_G(dnqez2bzZOp{ z3;!l845uZHS|`3BEOeAB6`ZZp9;0!xZc2b~FMjfQs3DgbLyR)v^7E4B+S*E2y-3%t z!XV8|?9i24W*#O!QYUWPpb>l>4!#E0fF&*Xct%BiekhAS-!Ek3j*9XfB8zm)(o{!R z46F-Aq#eO~+N{yS?8xGd;P)+_fRXxrve24_do&|ABd*yfRqv{9XQ;V7Vq~P+`8=%a zsU}yp1j=J`z1ieoJJQNI4peFXU0MaCdPk3GPFncd?st5H_;^f0qi+!Nj^7|2 zhJU}~8^nX~xWzY!hw#l-zClb<`35lwT7H9=5Ao4p{V3Et<{QK;iu(=X=0(Fdi1}8e ztxO{zxfWS&Xvf;%8^lbZ`8SBc7pT=dJN{y-$rUS^&G@hg*qBg285Ol@SdKY_eR6%p z_B8z4dka>9hQ}M`WO8|@0Tz{AXu&|il_jK;dX6b&Ez%ke>tpQEMSCiXAmw422RBO- zbAXC?ke<@LpS>+vRL@CHW@Qn&j-*g)-$lkwd6Gr7-ulX-Z58otSnq-=!}1R-RorI4 zKus3ysEnoZWRi_}8YbCT8cVM%Oiy8ci^;UU%S=wqBXR67f22Q3qF9;{H6#*BKbWBA} zuFMga_eI-Rj?Dql1BB?&(7aR&U%6I=sfi{J=gVUyW!B1azNI8&4v+7elab$^jjL1sb$tRUz6i6DuD*l9Ny~-X!sH zxwIH^tHfd^ngnxU7|T%DDm6hOA_|)O{rC#njB6FL_>h^DR3yqIgalx}$$}mX6E3Nl z!4ONavX-zh-k=*rL{ZigVBVjHYE;ED3OSgK;~2}?GG(9%5BuTYSXoz$RyEK>K&dl)$BlBJ1DjWS=ua9}#TRRK|vO3^c52Zmj; zd4lgr=f+|*sxRG?!b5YI#ld2<=B)p~Av<^Sri6yQA>3 z)SH^we3iuMeOM#G9cP_rriz+O;beuD6LyhTPJ91142+z%8}@dgT+#jqGQRYbWgdZBXlxk~oU9He&0IHCWR=gz^W8|vYA~Zu zH0haOJ}pthR;#NNHJ-9*L>AS$#^&jcbZS7z9}ESxTqor}oP!&*r!bhSEnDVk?+_7^{RDq=p#5b3(CSPCi7`RieANpk|6OwxBjvb^a7feoYO<67 z+D2Xvx4w`!#;nW~)^i$kiLv#CF|Hu<0hS+W;tq?Y!6@D9L@71EdO+Uw(FDclTsfcIhu9nYeF;&K)A96ytdSiSW zl>}_Vk@4I)?dsOZ3TiaV;%Z^Iiu_}F6wU0M<&&$VbAvDL1kElq76#{}LRM`}#u&YE{BM#8v zE=+3s=*{R+F&G|SM0qqXgQlwsZ%W2jr^Po{#$QgxYevRb&)s(w7YX~sS69ZLOmzxx zplP2>#-FT=uabH8s;06{O=T|!kF^)gFM7KTo{ZCF2uWHkW?nOrR>kF?aLtEq7^c!{ zTmMaygta&=Srk+jtwck*`wF;U$<3Fd+Od^Ik0j%dRK_2yD1T698V~Ayg$HF{p%yR7 zzQQAVU!gX2Dx|(LUX5D1305bIs%4J1qo6W=U$W@F%A#cz<;x_&WjeuSk|6r&4oUDn zognU4rNwKa&WgL$roW~R2-Q_;lmUa1MXRdf+q~?$AzVn*1Lv;b?;k~cX)0?7{;cou zmhSM+glaLH(*aB^F`eV|DJNN!i>X#+kue=}m(!F;79h6K+W#^QNFF3lFrtplncEW? zW4sOribZ-9M<_LUt59BZji*{MIcFU_$=F{ZjOnlU8WTtj93TeD`w;X@X>=!vnZ zuA4S9xyacVeEnf4_$@SPV^G`T3FKVA|2~*dQJ$^JFqg=tR9BSeC?2)YkJt0DUgg_u zj9xvJu_V8s+QiWSlp2$%^$WMlbaYI5Z#ZN^WS{KHE$x`F-o_KV0eqD{0!Gzi?Js!3 zXu50^MHU7n_tc5V8Zw1FSWE%%0Q3zrRWL+&kr$4>n)L9ny&Hos^w=E4ktnL68#$np zC0X~uqGkHb_OJlYMr#I_QI+Zqt)>93BzS{06FyTJ%WuLs`oOusSzZ@LnGsl)FFPZl z5`F_6ydO-;AWtWjLRr#hiVDObnzXcbd{`L7^iz`tTwr~j0dG_|K=cy~N$^#LM$23m zWCMLG2|!#5A(KuL7f|uNoZU(iKmEQ*(DQ7RM6Q1A+u|hrYgNWSM{#zkMPcp@EpmUK zq88>3D>EJ2`*`Q;;)?RGo60bqN9(`<$_OriT!JpesH_*`U!(0Zf+`(}`cxhMdufw> z$rj${$h|PTIO%7nCpKSLTqn4EmC8t+1 zp1U}{JDL`)LhEQhWYR~d3Zdm79FR(ChfWA~RO zg&Bz|JIrQ1bi9|%<*dw^ zdBNCbv8+`_YHce;I8u}yIG4dwh(V_oud#SrB?A zvbUk-1CvKM?w+z??szpeLD8%(hwwJnR?tL0m! z*jX(fgvC~=r;@@PqsPK-6m33*9lBlJTTuKP;ycmTWrb9qoPacQ9{@$J*wdr5UENPW z6n5__Z2E?ep}687fw3RyGfwlfZaE)x$s$aEmfku5$CGNIC9s!JcddS%^3Jnh^{Ol# zSq|VPh4x5Um7|yrHi9aG0a&q5-E9`7(UEZo_&{`%3Wb3*vm!|6en^nof3GMRSnH9_ zP7I`bW_mL_@@z$cuqdw*J$@G~-dYsmUrti#BtMZYL7SwUZ~9kDsb<0&2!eZ>CpqGO zR!UfaL2zR8BtMd#_Al~M&1_(jyJW4)R-C7B_G$%Ks-_9}N-{LZHTzT3_oCN*Gbjwk zHK!cGj@cs!kK2XKm&(@?m7+qhGFlky)9hUl(YiyRCNwqHD$IC_>^V})$}g?0cf$_E zF`zpp(IgNz+aEu?`4+h~e|8HGo|4n&(r_7Upr+km= z|5hD0_5c37Jq7i?&Z(Ozvl}D`K~nGT>h2x+!@DnYTup<&;o&U$_xSY{g z+E=q0AJdS6kIa*wI@{b~3#pg1>ACm&y)G}CJ)1{B+Xw>&9(&xN!9#`)8$M?2I6prA z#0e*ze9FX0lTR%??esIwEIMn-+2@?w#;Xz?y+7@ui>J>hzNF;RnX_hJcKH=oUNvWK zY1!5D<}bMByWaorp3yK+>iLY@;dYSnIUQFi&NnmO;K{(p<6@YKD?xz3ZI%Q9!g5>* z0)#EN5(Ef51fLM3RNTQobMQwFJ^=QLp&#SmSq{F_!B05&6$gLp;3KiBVbgT*GzZ`8 z;D;TIqKW^n-{olE=r@LSX_wyIFW8DO{#+-1m4o@%&>YmR^>^pA5s$U0W z9={}vr%&gPfQ?V*e+t$9c;E>II%>y zS#V#$%LT)UCBizv_+cEv-vl2c_%p!+1ZQ+$z5@jxAsEgl5e5r}FE@lqf(HpM7Ccz+ z4T6UVUMg7obY3ZVn8d#%c(~wgf=3A6DOmTU4(M#gr!xnJgc>b0Lj;czJW=q8g89d^ z`$dSq5%=`zyjEx|iKY3bQ|q#E9u|A~t_dd17utyLf=yx4)2H(S;6(W1(++NMFdv&V z2jS3bYk9dew>mT*hnPN{M`QP^HJ{E?5Z_vwPlfUH>713#cYB8=jmlGrc)m-Y&VkTq zl1s2|XM74@FIdNaDY(DfbUGla`;CIN4DjI!O)h_1!}o%o`7(UuVBQ=0Jb^Kj|wM;(c#IJ?Q`0so= zzX=*swmxVZV)}Gm2%ON#t23Z(4FAgn>j&=(*8ES08Zz{k3D*3-%ZXnD^<;cHzavo1$0#!Sx zLgUl9vtS*6y5NMq!C#XvBMiP2G~^Y1I&+;g!G{RB-HEkMZI^YWSIaheE!&AW3DeF!3LR@$~6@EQXClNN}5A2u>Ib{Vx&Ebm-H0 zr_gBKo{1_MpUyo650f{?2p%DLs$i|R=L@jZf#N1#6$q2M%O>;4$N=g0)ZQ8o}D9bKhf0qkTHxD_Hw< zepj&e>6{M{F+QDd60ChXen(2Z&$aBEbhL3|$g@ z(3s$>&Z>QQOyI$UW>hrXeYRA`K@Rb@|rY^O> ze3u~~m%-D4`+2Gc=75xU0Pw9yhw;Rd@voNPAD#_QKCti4E%DUaH0UE>2HNlj({4AI zcDuo})eWY7ZZK_fgK38wOxv57wm0oGV%ll6#|#Z^FoS7#8BAM?n6?&eClgN_$za+@ z#I%uU3z>M@Kg6_uXy*{q&Y|sLXlQ#FOnbv%+8YMb#xR(6g~7BX45s}+O#6Z3yNT!c zPR#M0V<<7lP>yB99LqRP5u0&}n1SOIF~=#69mE_vsQ(QObv`k5KJ_^<_4&`xzWM0Y zlymyFuEx9u$w=^RZ)gAQ%#)2_e54bQodWCCDoN)^H45pxBI$IKblwr%-O)1`0K?!T zXVW)*XcMNx;Hy4Lj~>MRRDr}2_g51wK2~Y%&iLchMK*pgwgr{?82KiUKJ~+wf_sR5 zAXyDAlW#5XY+MYi3$cf3NxmAU9z|XD1+G>@9iHehj`Y47ZqxChA)@l1pt_R)!N=UB zpN7B%3Tj<~ug0oT7LQYU^do&-oo3_5tMe^BNmW{Win`t6iRy8SC#jb$K3=_MaiQ97 z@oB0KSo3GN!=Djt@`v;csTrl;`d4B?OF)tOFP>#>;c`syr)SLZmq zI@d~>uYMHHXi0yOx{5C%_|U`)H3XjFj2)M#r8fRj^?=2*oblpvX>UIA)fH+Z{?&Cq z%c=X6HtSA$2G)HuuEA3GtJTG6s^%1_`&Cl+DT3!n-KPtl%R@10j^I)mT@!-K!j%J- z=W0;s;Hw1=f38(fRT6x4ojTa!as_n|#$Rt|X}-F_=`RZ%S-Mf3ZRso2r4}zzb1c3Y zIV+|6$UNZ*AJyP;`v_?m$cO83RiQ5V>c{GR%ZJ6n2VM6{;e)RG65)fcd(v7NKHfFK zS9jRFe08T&_n#@KR0%#TgUH6XTaCBz_o&Gh->WXL_#VeH@k;c8Ro=~~4_*Q^FN2w~subw;!47*WyyGHP> zB5#`Kx3y`j@R!02#x!tbc6j@Y)9;>l+TwR= zO!$Vc)`qh&zWX91O9vkk@iU9psrxK`S=KLk>!Wx5myO?~-m~}>m9qF%^_j(gP`fSu zFXe^x^`W^sTKu}=88`-C{ZSod@t;(0VBMFW7T*3vc>9dQ+h^P4ZJ#h5@|L((c)L{{ z)n3(n3S6!JEWFkF?JvSxt>6AC@~q3VmH$Za)!PoAw>f(3J!Q0@uWDrtm@oM1eO0Vq z1^&Q}LB9ICqu)MMw_2J&-D7c`V;g^rmQm_!sry@P_TTm4yR0v9I}Bs_>T?yKzPgSL zQeR!i&m39$JbaU7-sRNsOGlO()j1)RufBEkc9WWC<2`SY#cAFh7N>hgT79*T7mZUH z-p7`vgSXS-PTp4*XL>nS9`^H0oBC>hZ;*{Yz#9py``S+7kCvHTZR&i|Gmuv>$JBws ztHZor8LB1^7&eke8%gu(P%jg>pTu|esPh>xZwgZ(?&-DH(ZIbtEU!qwoXG2KaUU0URB&-8{`JkUGW;wc`FP%`+Kzn55iuE#Id==k%zg%(fsZnOA&Zw2s3v|~aY?$K_X zAh?Uid1aB{BRtM4iv@S}_)DO9g7ZB5lA&p#;BFpItR@9__xLr6rGk(2_%rPlg7dv? zz`qvU!`lkXven=+a#9P-@-R>i9|ry%=<`*v_j*UuXQzuCzJmB_HACc(^gd|#FN2Sd zX_+*AHQS-N!W*wiKyxL26s3c&=6ay1 zxNYAGuZP8pJbto=!B>kte*aA4CEi4flis<&e-d6@BJHAiULx&6`WjrOj^F_!bD?ZWo^DzI=!DU)`7Q^e|PE;H#x!iY({-4nKeH^yO-&FE4lc z@(QOfKj`%3hrGxpdDu(ZydL%Lw)j^LZ&!M++xW-5zgzr-S8wr?-WL`>=+O>gfDPbv zxA%y}H`bt82nkSg+qZZLz^=i%m{jyyCRQt4>>N zcG}`Kr!BTPZShC%At^EQ`jZ#gfq!=7{IAY9^_Iu;rwqQ@=0W)JI2Vf+q<=O23f zhzWzQKJv(ueTP&3tQG$2ws=wauiK)=;r}{^|LYz8ztkrGnLY#gKLPkqfY=Ue=(7tL)|jB|Dd zU&UgaUoiOirM8(R_^N&EVI7V5ePVA~+#yzLamN_vrwqR86g$}RcHh`=i&czcI)ksW zV<%gDK+Nb3tcAv?H#E&bF=|uYE}u(V=yusDZK2y`m$ZfM7hgzQXdU&Xw1w^$yJak3 znl-o#uWEsfwr0Crj=YYOb~!xOGh0YePM+%5!B<^lrce1QFGkyg!B^d42k>8!pDr4Hb*<<>F1QEf2=FgWboC1 zST7BM2gc$S9~X<}YlC8!*!Ur_D=i)xT8( zeNq&Q=1NmypIDl6V$rQw1={_dd>&V`9vGF<@@i)Xy zvv^_bM;2GaW?Q@{R%-FhvD+@?~?8Z2{2@KqN%_463g74L?xTYzD4Y zU1i;aG#F3$j|7aT&Kk&(_SArnm7S;zN2vl!-%FijaUW+*vY$HB#`jm}Sv)}cJa4&w zahZ)Dq@uB7u)5904|VoNhly_av(&w-Le-nz5!|iW`j4>-4)m)2vsc7ER*I9e-=d8USquP1;1s^+<`&&E+E0-pIu!`0U zhpK4aI!uiP%}FRf*3D#1^(?_%nytN?^4B6c&OI5ZABZmkJx`FFeURbF2uCNodg%m5 zFO7ya(!qyjKhff`j*cCt7TS33G8y{u*by@LMD?h}C#h(S>tywN#9u3QIlh@*b;lRd zFt9GK;#wqinWVZOZ0a)6smmmiO7K;oQrN@pH1#W#XC-hocE~7izZHC;BX83Xs{{95O&z)S8tMEQs;{LfR)Z|Q6r+TB zcb2n%J=@ujyG)f?nk!`wk+(i}3l@d(Eb|gvuSuE9)SyFD&EKV*bEV8X1(!NyHYqZl zc}|%ZIC~S<+Bq-xNtp+O56Qr=B*BNq9c}SV)@JZgvPiCO4o{!)-CNWdmcG*2qfa_} zI@hYpEzM7yeYBt2`uZ5%e`0BFm%Z#p)G>j5Vb0%mKbj|fDg)ghq2^1UI#BQee$%{F zn`dd5ChK|+t|LI7kKMc1EI;pb__@^K=g-17S;u7#Kksq)`3p59r1G)zXXYwCPMl4! z@eeutdD!94qr#s-QvSP~IxcH-FQz0+lXW2;Ep>TZ4aqg@6^}_>iV$C|RyuWgJbaUN z`L$D*-#B%7&e=DA-m&F=>)3KHs8hp~e6`xyzgz3eYVthgVdcg+QHsEjUVRB{YN;u?s$=Jl5wtGY3U2pGZv3=&iIW}f3fk@ zOiI0rGV?bMv>*NDJFa#Z*z;li{P8eVlMP(0#yfR4 zRuRj2k~24%=*&$fp=EUNu^KYa;)(Vb4?b2#$Y%y0BhYyo0#8wwSbVl~R_z>|hSR~v z2y~alKSKFT{Q2r3i!V^o*^-OZ8XG@dZL)ZlbH3{`wZq0=E@vHkpzi!#4A;Gf3O-4A zI9Bk<&1|rJ!gp)&@24RqKflB^5%l@6fHxhkYV>|esl(6jW5-_SjNRpqT;1T$@D+G^y`x z>e4QzzPCE{HLF@I=bbhkA1xa7(`Am#+~e43_saQ3UAEiXv{lBjFbFKq1YD(3o)zjh zC=YIt?+X4S!B@X@%J7i0_V`O@j`Oh05p=!}w3+XiFiqx5e52(1g!&x${uH zp9}t#*sCiAKPKnOekXXP%voL${J1m!dLm4n+h__kH!M#oN_xi$75LF+=lr>_8g)kt()0P`6uL zr`B4WQmCNKrp*6T`&s<0I@;oxcY(!e-nACD_nxqLA8)nA9lgyK@9X{9;;-27sf}c^` z6FgM#vx<9y`GTKQJRfz8;OEsg;NgN-DV~oyMeuLco4`eaUr<|sFBJScwHbJp;MHmq z@O;5*)XTt&1h2)HeQ*{_@QZ3S@Gk||s8ztMWeu+O1PHabSYL*GT!#QZ1$wOAc^!^a zHEV#Y)xmOJLigoE<(!S_?d^|u8SvW*FIPk00baB> zG0?ln#vkWJYl(xrs}O%A@=730wtGLpy``Ldr3RPFH_|h(%va+Y0s4G3!utivtjE3~ zQf6KLp>obdpQ{`uJUm6}HC%YebbP$ae;KgW=hSPg*F{Go-gnOY#GNxg6Jr-gO!RPxcmDe5&^gi%;_&v-ouH&laEQy<>5abM|tIb1vv?=lt8b z&iS_=$@we3?yK|VERe<*cs!$`_t{4Z|IZTsA20mZ?J`QvqFf;HCy2~kA$YW$MPWKM zxZE}}=U&)Gry~6X=*=F@j|J!8iBfk7K1eYCOvm+Uel$OA0A2wa#uNXL=cnYujo`!O z7yM^_x*mLB_;;G0K7#hmhjmCfXWe{jDH0!I`xp1AJCgu`d)YlT1U^jmLv?%?$EH2P zu7j~Zj>Gj^$*Y?>u7|359k?33Gt%?j!W5Z)cPHN?o%PQi>Zp*)2O1kSZ^ntqr*4(UOy+V{tnGC_Jj+@6lbmPSZDA4IH}{8$TtBipYtguT!YKi zORPJC;X`aR@lf@GE$3jToI{*4m|c06bA(Nk``Vd0NyH!Tlyj6*&e2Xe3!HKe``+a| zT*~RIf7)`6m2wV2d^K7$Dg(K%gO8cpz7|igry+c-6dY#bPmz6!F-RwYba>7|^Dy3Q z?rGAg#e10QOCS%4Pm!_}s$oYNS(@bVaI%z5m-AHTw=IP_5&ZSBqqARl=d075a-OMr z+4!?;zGnWe%Y3TB=l7aO#`4U@H67(S3N6781kVF*x&HYe%Gr^0xQK58jlnb+Pqk;anD1G*{w(>PuGS)7+=Bje{v*N1u4h!oGo3anvTek4 zsN3PL1Z{P;ddsGBwoU|pef*j(O6OcBo%8leC$m87Qhr{X2~5Ye3uzktN!+<0ivJ<& zPx68K^H=B?|JgqRBM|{DGu{s#bKeMWSGUUN7^?3@!MK3D9VlLa3tI^jIQ zon@`{62XVbj#HW7!(pS~1I&WEh>pEe@DYyg>Iw=SXp=|w-jRxHFOL+^e(S8@BDd*T z!FjT-#W!kj8Twk_1934>R}jBgKzy{?rev}@5z{7LU3N(U6X(&&)N6s=8V62cHHOpriv_m zzSt^TPzLi2sJ{u;-+*cm-1ax1jGe=NabcJy`vtxkX8Q&4S4hXi|B(9y`M`dWwV&=6 z>qDOX5WWEw8>{;Q<)I6(=?nU|1OK~yA-7pyh}N!i9sBbzXW#R1*?)B9T+6nWb2dW_ zF4Na)fsM^f**2Im{3}PcR{|e}x-g#jhb-IV!;Rp>H%WX;1oBN>2!ZT-t#=U=bsp? z1KwfYRx=3yp1qr~_+FaILwqmT2>Zx$9V?<$vw0}KdxT5QHe`oSzT+S9FO(a63Ox^B z2lQ1yDTKQyxyuY@qo}x>nLaaZW(>|m^9Pux+Lg~u{pCLOE#5*}a8L8VokJtfT-MJA zJVlG|BDnPIs{I(i4`|GKC5K-G_@1THgw)&OHv>*d;duehHZ$RiRJ>&q&;>H0nHXc? zwooLz zHoT0|1v7o)!t+@g{KJYzW{sN$GrQm^v=i85eKl*>50VT#u@?p_WAYq+=imtjt~mBY zkN$)uLcWR&z@lAr*uF=cJ%tRZ8w?W^jv4TmjwV{v7R(SAd~g5{ZxTXOw*nQ^H4s1V z;zt!uB#=HBFmIq+n`FnT95_+UWM$$R!`HXgAaJFERy)ZiFuE0v?dNi=Fdg2=wDU3g z+I?HsJ@hR>C)q9PLibY@!&K>X-Lh;6L`mDjxjaN88@^>HAr?%^>Ai)H>&9rG>U5

N@s3z+s?iq&sg~Y-^6`bus4(W7{(wQ9<5f5@q5#eq;BX|(6qOoleCGrhy(ju~A z)Q=%bs_Zh8wlwUh*R!xId(Osoe248d=m0Hb%(^F$PE2=$(ut+EwiCkx4_XR72g#DK z6Q3)+Dia=aM~=-Xn<#CGW@R@rf(NhhgQRWoG6>Vt#-^ijrTH===Korp%%J(_N6e1ELiZC1zLkbB7gyiV>cWd#}k0p8fxYxP^q9Xk+voNB+G^7u8o4k=tfA2_k zf)+=#GVUtoXQrK85)6UhS= zhjxrC-e-*BKqIwiKdFFK9M-`+grJc=m>N~qO>ZDszQ;teZh46Xqe6korFXed3L`t%gL#$ox~&!ETOkh{<2(Y>RJVXykdAnp%C-jlx{(mkO((K5E|#|j zQ(8Q+?+?cV)S(A#fu?oA!SGEbcgBr!{l@qXHeWIxNJp%Plq@U{=+8Clr!PPn;LLSo zJoCDCb&tzUzPZ7axAbhc&Ram^y1CtwEZQQDW9#AOc5||5b7j${ zit*&E^q^D^XLGY;tRU;)m9Q-IURDKftg7yQ zcB=APRjPgbHkH)|cT>2v`%*6`#@dl^Ya7{_xk#FaW5u?LO=*?s9Wh9T9_dAiHTKl@30@)1!z|Y9QMPPUvfc-X=e}-x|=a#B1mt>uTY@eb2h> znH{!N#@~eBY17T=8L+HG+hyxQ&AzaF4>@wfkzj>wck3n_-Bx>Kq(>|RCkc+VRTwHOKWB6uZ@lt2!hjEJhAWd=u%ijXbzYy=+M7J9JE@b8PGd% zXEg(*1vXy8-Yr`g1X|eklsVms!44-AZ5t&^W&)^ag{F^OQ1R{xTr2S)^ zws`Wan~d6rVjBc=^pjvcbeiqMJ3;4Hr!hw=#!?!`|&4>ja6V~B-{-SYF2=GxjySG`EruEHRtCt7*exghSx`P!@|ZO%_J(s3_kdvdDv*O?CW9Ftfvmv?F*= zn>AXP9a-EF{JzB#FjBwYD2x=N0o_3}a&t>Y*1M|P8ES5i7#Vp_cZQl%O|EPSl*i_J zv&q58H^xD$U8jON8jLtnNV{~x3?jW#$l6x3tJSuUWi3=)UC&5lcLXnLf&fAZR@dS$ z`dqDXiHzpV%G;_ta#r`Pp(Mk;fJu-UO#Lhz2%xVTLoeAf2}w=Hi*~LNJYxoo3~^`; z+0j(JED3G7J2hvTr2{wRaNx_4tVJi9iM3p5`-X~g_*S%BEES1i3!)lfC-5VAXb4^p zT^+)}Zf2o01kbj3V$;mS`y%Czg%UM})Ap|-*RT^ce;#(CGfWOUU=Fx#zgCKiZfe@T znRM;2h5_cPQ7bcA5(a}LS5AXg;kUTpOUOi#=hVfzF*te32(Ajb%#qfWDMv(CqIHml zl^J%&UsG}G(YYZ=YJ%WxsG>+_jBto;$tgeQa3xsasR@SaQD5dFu~hHS%B3kR2J3*5 zT2k$=sqj=LTaBsLbyC`9M_M`fhH;S6qgC5!6_D!pn#xHFjvr-BC>_4Xv8YqLu>9L! zWL=HYWIbQ>?ec0&LN}zxRMzwHZ`0cGuByLG-u9un_*sR1mTN=g?WtAG8dQ zLl3N#>(^~hhp+8>_-1JO>WBF`Mzx_t=-#r8D(+j|REfriH$=45ht zrvVm~U1&l7;>r@zNj)c1(L#*gvU~;mt+EIb9;SD2v-Dv9SHy$#luM(lHe^ zxiZsRek&TXa%>KW9w0=&g#KjbKF*j|j>gQ@@%O*tNOZIqCs=;>Hy!D3uhAtt|>Sik5253YJ) zBB^Y%N-JYV3rhxSSEVL|iywx9MteUtAli&84l?J>OfpFwW#j`1zzS1b7=wAig)um& z)vLl`cL^QL^G(rKTq%jA&O^u2&IW7tEX^4sTPu&%@SyFbI9O5^u3$+SvASd}IKZl-BZRVX1TsR4>uJ&cEepR*+8u?L zMb6aB=4%~J@54$5?l^Zu@)b3i!pRCPC+s2zHGh((d2Bj3mQ&KO_K6-oCc`QuvpAg5 zOqN;RaLvhZ$dOZ9UTteQ()pE@S#Zs8-c1=xru-a?mgkaEjwfZ<{ouIDN@3xq=k5ojw){Kj@Gf!Mz{wu4iG8lFbq*zOtobQKV3 zA;}Lsh<{h{B_q4w&`f-S1Fr=9+N1PODohwum||f{sGoiKkLJ%{&ha}XO?7u5Euo{v ztT9y7m`DNgEmWy1F#v?C7__T;U`mXfD3MrDz_V3c73hP(pq2}C{D*UCgZ2~#cW4Wh zIblg7BREUvB=@?6zK*eKs6lGzjKs^yhp54hk^wIZc_g!;$M=lP#;OGRDOO4KU`shl z{SoaxGdtj6XJJi)oo9&Fsk;T$$2h}OQ~}15c*DkmbT&nDY6D;Of}8)YU&UBCE+ZV& zw8ok&C4jz>*TXF=|N~E9}L9y1HW^{spKO+L(p&cbu7H#bSW~WJ-uTEnhj zMT;jki-)538-nW$RoI8j`whWGEuPr-i)E?FS=y<`hElZ96JX?sCPSDl;hC8l)$$o_ zrph?kU}MlLg}&WD!quU#FKls-2Rhftt23Ms*R&Gtkw?dtmh2&_~W>+5z1HT2a<<5jCm*%Ai zbLnJWd=u-RPA;tyzx#1mq>$L=vRxM5ci7dStq}(n%A;EQ=*{R6f4?k}gDI?i&R4^- zU174@Qc=D|m0|ZldR?ou8R~J@a)7m_CK;dN?&sZQYyH1U(k_>-0KRWhGm)l{~r zsqE$8vG&qUMX$HPlk;>LLXuXCIjk8;tKxD{Gm=)@`g8oESyYD9;qdh8byx7ukEGJ!eM9hxzQ zl#9t&Wsxy1bC=W9NERTr(asR`D&Rr#1S9U)oVh)b5yo?C+PH(E~rVUYQ>c-#zT{ToeTPsG> zHU@h>48=Tr0y)?3zYiu%RWxE_sIPK&0e5_aVb{n%;Pj)QHFQ`^=1OTPR zbZh;>?J{8t4&L5$A^VMOg}YgV43Ob46OLX8`V!RWMMrBO_RAU$OigW5`efALMELg z=rjfeY$b`Ge%~bch5jgsT>aX&#YwDTRmML@adxRiVeSnra(|zq7Um8sG#%Ucc<1Zl zit?|U$}r(a>%geU2rhtJf-c3VtQX^7qwO++DjkXXR2}|%X_I})7T)K`y)e5t>1U@W zHeXm==^vVIY`+)sK);HI(?5NoI)dE@^YG`>7nT$D5-72-9!xB%3n;N(6sw`cHbt=l zzY*@PBE^zP8GowXy`{KYyTvG~B!VS8;vPbZ)#`n&&nT|w!KiPf6vfMli`NAiS0Lymu`l0C-#d@*OjXwlB{j7bC>iQUEh`sFPOoG|d96sIe2SSSM%3eJF-O=>`70b@T7i$I66Zx=Os?Dc8!JxdgqMt5Ol z^c_)Z4KVY={GlD0zFp10CJGbCf!2})mc%ppF85ux<|7csRmnvceL<9?bM~AVWWa$xhXM>t2+DWl8SmN$_Y2bIbm49 zgHx<8_m4}lL%wHs?yYkgwEO$(}YEoup)iVHQSCwLzVL!+%4BiWOU+t?YoVKLwZD{$xB$j&0 zl%b1ThM(s8}OSxgeH02J|jBL|D&n>%G(lQ*20W%VPIx`IaemR?7!ru~q7+ zq%guT$Q6=Br+nr6bD$+>qFU z1s`uqRgPjh*zly60G>d}N{(D8qz*>)Vc48pi zGt-;dk!L6hghhFk=<&N?@zx^Me>q8|ll(*$Hf)k|j_F@5rJ4zAAPDYhp5%!CSt(%w z2EmEVll(|}+P}z4HM4<9?vk}ITXCMY*{c;`shTF>E6LCt*X&PC--}-N&7d$C*PL<$ zJ7$j{JXRMrUn*Zq6i?s4;0hd@neYOH0L@f6u}q^1dm74(eY-LM03 z442_k+rF^bWrVZ?Z=>!nr4umnro}V$SSTkSCuMl+WH`?B)-Ul!#7Z%`lR9fHj04i5 z-(#38+LaoM(T=CaI9fjrBJA3H-&ZlV%BdsN#~tIf)v?DbJrV=0cLU6AEZlWW4Zy## z(k`i9FEnCSRgZPT%2+C2Zi+wAcL&Q|cmqoXY|tAiQf=Xdo&*@v2AOngmdVkQ$)Tz4 zk`hM0Pg8jY7XKraWAHy{2(_L(S7`Af_f#fjC#U8tj(@Yb=U z&%P8pIZ^&~*Bdet8e_q9FKt3h@VL!MdPqu%c-~|F$UM2fa@r*`ct1V7pIb6*&WwvMXBs`GcT^qA=6+|I|I_qKF1cv- zEaWv{evkPbmA*sF1+y=`tVQhHORv!dHDs5w=yOdu`<;*{_1DRD?5JkXb5quLP8vEw zxG{ZN=`@>JMEu%;PMUmb;c2Izac0q3Q_eo;+%{g7=;-}v z7hODkM)4&jm(H9u`?AZgxbmtwb4$yvo;QENHQ)9A^ch!Qdhv|B%gZjiXokob#dKVyIJ?hygYn4z zc6uss0v7|f9uouz%W)+L5Vqh-5Fqde9tlATUYa9JpMWO*BL^P<8{Nc@aquh$-|65d z9Q=xdKX&kuSjn?#I(V9cZ+7s*4o1=Bx0}Dq(ckvC5%&!Iji$l8U%@{J=Q{DL9L#5i z<{)h7wf63uHsY}!+bVtE_N~V+3FC)vQtBgM`Y~hpr{F^bAGirZ>eFZNU+)wa2!T7N!!rufRBlt7H0|aMuV7>zdA0hZy!Gi@K zCwP+JL4u0~4;Flb;30yS3dWCE5mpKwCio@6!v${>JVNkJ!N&{kfXD0PxoCj09HQ9TijrcA&C_(xGVAlfR zM3~jn4sLKTADeOx+OPGzT$)=Q8uL5pS&;Y9*b#2+chXZ3-&*;f3gf$X#t(_I`EKv9 zq)~Y)5zlv%7vZdl&}foNuvVQ2ziN2BU>*OZU@c3XuzR1-(dtIQS_T+p>f08+7xbpg zA34~i=l609O{Iffnz!4C9}L}O-gW7hv=P58j6blGr;fo1p9Eh*I1B0^5o-Ebf_3~| zg0)OO<;1Ur%1Bs`V6F4vP0*OK^+DSZcP;SLLg0i>UY!ATWB6YtSU-4Qu;za{)R3XS zOt9wvT~7QOs3(3?pMgJ2GW6r1suH31sHuX5MEz6ndHM!_SA^)WuDYB%=i-avv6 z5puf|Yn{e>L*F0qq@P!Sa}puu^EX2LJoI08sNjSe&uBG7@ZP$G&sOjc!pTCT@k+s3 zC%@#xpO?=xkJCi>Yu=rM_#1Km5>)Pbq0uA*dyxKUxfw55$CnERaTrWGcOahWm|)^R z2;)zJJRXZ-BM}nZCK!Sf21EZ##50{Io=sCbg+}Z4OjIeMv@G=$JWSpkBY1@1se-lM zo-glzG)=sO-CoQUBN;`lLnw zy}s3V8`_s&F0Fx_F9)v19EW|Gf!_%;f!_&B3vc-SY}0@95eIWtJuT@RNuO^uFH1Vz zB%Q5-n}3HdN;6+YI-#fY4dy=1cT)SQg75`?m2{%T$2xxMj)SL69q8+6Cg9(ZZvr*r z682|;d$jot>Dl4CtP61?==0SuHQa+gKunx=UOTAF*fxeF#>&`UZo-MbhXgp8IG@{OB`QGm#POW%`El2#g|jICHU~-x)Dg% z{Vb>MQ`)RM=^0q}&A0|g-LFn zU>&apg$}-2;PB^K=UcPag{u*yFL!*YU2kV>^rxyVFVZY@Wa&nH+18|A;rPj1WWOKm ztDBLtQo4`K6Q1ym8eFEmYJo|^Kt5cLYccAQ4=+VH5ghVivG76Hy;As~>%K(zpzEF# zJ}@2MdN?A!!{$XlRQr(t!H2KZ12qI*rbb$PcW9N9<{mD^N$}OZj-T86#ZM_8`0z4Z zu1SEa9p9BJ)MGaOLG`S~zf><-{E*|9>|qty_(#>(7Ozz4V%agTCsZ!*{ov10s!H+e zK92#zh7{he5`3%3o96j#ZQ3gQMLYv}PW-a){3(@8H#~n*c&>HqZ=8Pjl)T43K79U| zcJ$%Bj=Gw`M-RcYR)Vj7C*RuVjjz@^e(+ult!Bp8pfz-$Z`J!OepzfE-qQCe^uGij zp55NF_!X71_*KXE^&cGH*Z-x=C(?Xqu8x+UuRFd#{}@{C%Z|McnIlV|hi|fuyPP_H>Bv$ezLc$lufBEkb`!ion|RM#WO15z zhsEihkyc;r<88O`8Q#Yhckp&v+{ydO;!H2c%E^A7X;UAb(+Ao31H6&Iy07gN{%D!m z)uzrTJp*}#r5ClI@aiyc)jq1`Fksk79&IGet3$`~`4H1{jj(~J6GFUMEX z(T=aAK8~-XzK*Y?{*JGtW4wr;108-I>+o=p7xlrxj_;!(UQf%DVP51zeuQ_5jUVY< zWbr6(fyF0y35!R2_gOsFd&A-p-d`*p=ha&5dwMoZp2WS6ZTxueGm9sC-&lOA=fw;U zPxJP-_;jzE#bjZc6cw+lz!QDN6b>&vUM|%7m!To~s zy=}n165PYv3e2+A;4*Sj3(WE`P!1mkeh&2cs@S`rqv^BLMGjv^d^Of9GKHFTFAlj$ zJkycA*$&MW-grxYC4MxhgRka#nAc12Rhf5<#q+%1SUlf*!QyMY4HlPs+bq7p>B~2I zhuik8@OoIh$nm?i*yDHp7<{$Fn`m*;I~SO~=o9J^X&24&k~aOGk)DBky8!sFcsE}y z_11PWe7jBf_5tFn)$PI)-Iwo>{;T`)onACPE)7#;+3t7v`E#c)S37-qxzm?dIDPp+ zr!PO`MK;O9UQ$ZT^dI%^w)j^LZ&!M++xW-5zgzr-S8wr?-WL`>=+O>gfDPbvxAS{-?t_f3Ny?*bs#RjJ>HaTtaiqjUaI&HDpX^YpK zw%Fpd#UH(gP*w)m8{VHZ1pc!l=YMs^ska>ea@#z;%E)&&d;Kh*cQ`!%&^zA7ekkv&Ou)J>ND@8{S5ycZ_j<%HXR`v4bsd_l;pLAI7T~$8-i?Wyg&F zJzpIVGkU{UIWg)DO>FQhHBj`~vCLidZ^G8Qn+ z8eE1~wZKMOvt2GnUj3zA4v#&bC8Q`Pf7POcue!!epYl~+EYkVi_=8zBTI8oojPk=b zYH%6)T41Bom>2OWlGo9(KOisN2YbdM+oqSKuk~T?SY+EA?UbjVQ=a}YZ1YHf|FKvv ziwDNy79SUj=4*pum)Q6ru`4Ye8sp4ZkClDetc#gFvo6=-I$P>;d~AHSsmpMuE+d?} zjCAU9yi=FBQ0JxT@cFS3R#vCQCRu!O>`IF##O7IC99w8{N$fU@ zXU2HOguz#{V)p}UnI9$m*KKh^jKAyF@;^Gp-;Hb8F5s64nzYQ1iSdM|?h9jMw1spZ z85g51#Pn-$xiZPT7}$<0fTv44&WQ#43n|Jo$7!Q7r;X-0?YJP8$3zf(b*&?N*Tu%` zXvE(TJI&&SF})K)`ij_W8^0)4YVpmn+bv!kyC1j=Wy2nBn=<)}ke+oRUI=>Q&y$a) zmcw+XAAL?EM`EVoZf74!@@(Xka+LwOSDCewObH8f2 zRwr<;;b0OVaIfJ=4S{o=J;5$=0_Y*6$@5zL6%Wa3aJ45usKqsg0D=9H_(>^4o_Ymk z*a%z=KSdnx7%-m7zZ!RQ)eEf1U*v_pfTkM>P=e(cWbu(V{C(3(7Jl*LEP^U zK8#eC9cbDD5n-TzPQycAjZ#@Q{sd?3dNeqv1AWRxqghz-dr=2LF(CAo)NYcr*;(|M*4&BM|y~e`ti(z1RvV%4;CM*{%Y|c z71^SLRkZFpRK+~=?l3vy;G_IlHEw)SqyUyIlF3*V)FAf5{PeC+N#kz;r= z!qLgDUOK_iOQV%ORYd-ban6T~b#&}FwNMumG~8v)FnJ+-0iF(p)Kfh`jZ&Td*jMXPKAadPT}yrk0@0wNlQx zQsz$umpWxODKedTPMH@tdlT2%IWPA~nFj;+Nr!6)d}DZ;nDIA>ouFwJIdXM#coK~? zx4^@w4nB6}qjij=v!`>dy4=$I#MwvtsjaV%(fucu=62c3-iBk2K5eT*b%fvk76ozIw>v&%+LX9u@u^Bjvx#spGOX_hL$%x)6_$x;(CajJoLc zipQibg@~_KE1kML9=^$P{@SU_Z=AY3=j@w5@7Qv`b!@p8)Ty>!tJPGC*E;*@FG}C! zBkqkxcG^oQkq$miW<1U3(!NhPb^mpnbtgRo+i?}H^Q7(@m7l9>E|L0fkh+tGd#@(= z&_qTaeD%6>=41;jM;(0ih9l>Hls!tVTQ;?+TZ+PWS)Oxo&6V=JrGAd`@R6_H;y)66 z^^Q}9_nk8y+Z`S9fm4PZ&Uu~>9eX%%?BRN+zkK5CJAbO4v-t+j9`t8+ht^j+oxS_r z&RDibWnv6sz#68r*Su2=v+<3xUae`~QD@lrZ`Cy5CE!Cs{Z06$+xP7@+w|h_UGk9l zXLz@z-*vBZWE-agKLDEPpeOzz+s5PrZR1b3{>L_6Phmv(ce0HiLHp*zhNPT72V4z1 znYIc8&kvhGdpIq;;rZcxE$*rgu{h5;E7jfcYnqP}f;!M5cV+6WPL*U~@zIFTw&UwEA zoZQiYw(iRok5hlKm>O59w@_yO-huYR$ASllF4`q{U^AQCj1jeXkMA<%_*fb7ElrVQw@-2A zGG{wy)y`4ZS^9I;T^9cc<dxQDaNYY@!6yk1M+rW;nGI(6P>VD^4bve%zr=MS=<{I#=N+zU^nOaI!_V(y$6n`* z-Q|v4-Qd`$=GPLGs~a7D-sIS&iyYhg7U!%{rL`Ne|1RgdPM11rTU6Iw+oG&m4K7!H z?qehZ>$@D+kEFi0sdcu#w>tGTt6D7Qoi-gGE!u~PAkh9Cs3Gt@j-7U|oNv@+yS+_Y zWnB0!%QFGj6;hrRszVnkI?`MbQZTPyI%RmsS$q7YGsk&Y<_J392inYcjFT^Mx#atV zIvM#c0j|bull|y!!M_rF^;gv?ySXa zPz5%A6MCnidBu^3S0UdfezQH5>BEYO`q68eX(03Q>xRvi{tZ|?=G{Lz_SKsr^L*k% zLq&U7e^ptYp?^!xG-{f+Reu}5O^vnqT{YR_T6L4f@2lTfyj|&E-Y~BnDmq{EcXde2 z3w5Ez zyA^*kt<(8R-DC0BYK_I;s9K8~Ra&~C|5hDkvGV#`?0F|!+|Ikf;`ZLX7I*Mov$&(T z&EkE%dW*BYFD!mbeQWW7UZ-}Z3_0Fm79Z^OvG@@0Sl~ZGRuh39}51B z=*-UrKP5WzTft8&?iF?z&3B(s+$+ox{H)?$VOPP=DW01;TJZB~8}J~(s}#>ojTih| z^(Jtk;1|>u;PV9kPHhG*7Q9+*0-h^)jd~e4A$Tpmc!e`sf?rgtfvW}As8ztMeGRVm z1PHabSYL*GTtk5$2YtRe!o%+fYgPecO;65I=svE`1a6S{!{qFQ?&F7hv}kl6@8abF zGr*n+C7ZarcLd_z#JdTk$ua1C!H3Ct1Jbh$dAL4Be7-u`TaGg9Tfj2(mNMWG(mC2m zvyXFTrmwd@2@vqx3NKef;N!e#&ti~wk&PeX&9Zo?cNOpv1uR=1Ij_=7a9=4W$!c)9 zd?P&r%X~Gi!3D&lyhDyOW8!crvo8M#Ie((hTaJ|TC*wh1t&SHS;&qgBlv6L?saM>K z^x=ulS)d8dS)h}gvp}afXMrYq(c0-`kL6_W)u~=&dz|Ji*3pPR-TQ^bXL^rWT;%=P z;whf7uXrx;Ya4&Ab6)5?=Um)W=Um(caxROn_?T^)Gdx(M^LTbe@4ufQ{6DRLZ82K- zuiK?S&ZwL#@nb}0W(poFXH<~nUTtK~!?2A`Mf%r*-t67nA~*+6l)7E;L4x^bI@^e1CzCC!UPw$Kzs{iYq~Ya0{*k0m5=z2?B&IxDo^i|G)o+j(oTg^|<_k|IAz0 z|1b951y1g&yc<88Y#@n&ED|7Glnn$(AY^xDFWF_eBw?2jBq1b3#oI7DGkZy9XNS3L z2pU|7T0#Jo`meNFjc9GPub0d7=&P0ft8Hqr9U59e(>ATq{eQpDbI$pl zncX0mq(zzEC!2ZBb8gRh&U0?hxjd{;e7{ZW)(@{_KX$UAoU?4beg;YS6yFDO|N1lr zz{frA^ArSpy6lrG{uRMo_my5hwiuq5cxK3 zJnO@CcSPqU0iBlxbgm5O?0VjGp0bK`;v}`FbG6VJ1AY%`G@^mr*RK~Vx=5#SB~DB@ z{8f+?2fs@8FD^zp<4A|+AC!lERclh0&R#@wJ89%0;VXo;4d&CHwsipy*9&b*=QY8d zmko%iAMX-I>-~)Zo!4R4(S>=n&$kyE-a(+1=4)u8Ws;#tPZoa(&~q+of)8gG0N3h8 z&p0<4UkJYyVbX|4_@z9)kPjSRQp@V&%WvcO@^;iO&rMOzQO)R0T#rEbj4EAd)E;@O zTR)yzVjJVfvq>!q0$vu3HF4AD@om_h){l3bU+>|I0=>b-!9A}_ypE3fl<@a^Li0-V zF3|i5zjwHbv62DxiP0A31x`r$v1TZ+`2CJ4C_$;}!m#74s#{Uyyv)n!P^X zD}#L3+EkeDRYAV53igPv4)nL{?8_?*zuwPZu@>-Wx|r{)@%OmoyU}!>=jv!nNayua(m56bJNr^TyUzsD_{$>AxdD6< z!Z`d(x&I^|ZUbLFj)qY`a|K4=`)!(^pMm;ajB`_z{|f-`K`+C;NcHfi$ULW8@C#+0 z(KNk4q zBC8VupBStcRA0m-IB!Am%(cN^p0S=|7J zo)7$V@2viJ$Uo_M>S5?@ga!;o_}(C}o_Z+=th+ex5Lj1$?-Q7-3%v0!@EHO>BCtx^ z4+Z9KlQBONxLM%YeJt-HffosUmcU&CpDpmU0xuSLNZ@k>zE$9J1-@5c)p-7If%V47 zHw3;w!u(j^Wdh^43G%&A;8O%%E^w>Bx`JIJaI4_oBrpa(d?kV70`C^MP2dj;+%E8! z1nv;{dje~#*e9^+3}>t%4P6rERDrt%jthK|z-tB8osERRmk54L;L8NQQ(!FY@ZBdc z{lB*>AW$kaGimy?*9OctOzDL8k$Ky~p%&xyQuti}6QEd`Qwf-K<8MPXOKnT{_eJ zM}f7^{5OG5mH9^FRSa{Q%-azRd8y1R-0b^9&cmHvl=BBSucn?vrHpaI`&{P-=kqTJ z&gWlfQcN5lH59`N0&We??Z<;RaN2^itR2C7O0DMOK5kcV2cg^FFW{N6zxQE!vaLb(YrSt3)ef~CO8B0T9F|J>%~cB?L`Q`J~(^!Mn7-knK2CK@?nqFyvoC=;4VWt z(1Q({8+@2yoXXd)7h3Fp_3)tI$?wHYfQaXL6W!Oo(L`_W6wGzLUP|Uo9xj`-hqsxm zhqs&PeV#X)+dTd)=7S#IVLs|%oX+s@ZRU#}-l@h5KzA>0Cj7wT$IZ_?ybCWkxH8^h zj+x=$JI&c1zRPra_)hbB55Ln4c=+8WdWYv8v(4k*V}94e?=&Cx@O#Z?J$$eEBH$e4 zho{_VVNd0Dy`gcxcMJX+LkoKE7kGoA5jvIUYYolU{juOT8rsQI+1_NF1-}Ut$;s7T zz+bZYQnm?y3UL?XR9ri3u6!Br9-K*IU-W%}-xu_0?*~>t-h(*C!+#RUz|DVlUecY$byFC08^H~r7%>0>$_nW`)@V}ey zc=&+%2M_<;9P8z6(#-R)X?VGZXEdDS;l_qY=8p~Dp?X>H4%KlDc^|&1;Vus^Xn3cG zPj1-b;e`$Nd-#loKlAV_8@}n`vl_nT;qx25@8OT~T}t_SO}yb39&T%xIn$VbfUJ(2 zKa_s{rviUK`gv_fAJl#xo!>a5`)3#nju-etaz^WPf$x(uS~^~T*uWaIZATxGGg{1e z0)NL4z_%BF%$FbA(QLryAsy`7HeB3dCOQD`F&z!jJi)z2#Cq>(aOv=zPoyhY)zFNz z_~8xJhO_aj^moY_n3Y0j_aV<9og*=o9tWp^OM)J!`$_|T71AM|Fs5XFtaz!d@yUnV zQ2y_t0{%PJI1eJP7mM!eI+UGrTFO8_V5jf8Szuk`gIvg=n}rarKJQ^yGr*tf&ss2y z(=~C9I>yAYSMJsa@%1NfyZ=i~$B{L9zY_kh?2`TaKS0sYX{?|btY2LX>u385#) zQvXQUM0gBKl*L%Ruuc6Cu-jwOJN~-I#4LFR%zf+=T$^RvUt&FYzezO~41Z>DU#VHn zATb!%u;>ot;~JK2mmk-#+z02!HSF~Y0$yrvurT|C4E~-GnlCgXpt)fkX}&<_v`X`` zU{32+P)u`qKyxghIUeXI+XMPLf;(0#3maAcm075 zBo*wrWP&>{13|qE`ILI0=}0qEe7x^JpdjEI{fp*!vn#k~R1EGJZ4cfwelu24`t?GS z@@WtEn=g8JhunAji16VJGWLB^;5Q1dz98_MgjWv=oDg1pQ{Wq9?#iSl@aNjvUce9I zk01F)_^%OuF=}afT!Nx3w%{G#S1Yu0aG+T6ArJ;QIhduyhU%Vd|Q zOv~nCrci7t!2)S+aCEe#H&fhN8qK%#6^of%X*iip^leys-Kv(WH}$n_O>Q48+y9G& zREs9kVg^&Gcp^1Ak{`}y(on+XN*9%uv@VM+y`&&NJr~Af?JL^5+B)02dOWzI$E0A9 zRD#AeXnbi;Yfmf=pX_LEu-PTlroL@U51CLoR~#P9Wzs%MyGCh8g5d{O+Y0~LY^Fr+ z(8wLnCvp{`ol7rSS?p}@VX-?AI~L~c;ht7cK?qoCB*7^ptq4+HF@>&Jn2v{gU@~U3 zQ1U$KVj8hdp{@(OY>Thx?CRnFV7LaIl5ckqryKe9bar&Kbs)I1+gD($l>xgXZNNPj z#^POClRn8PECj=cjWN-92a+7fjwY?(SaE<~JA6%gwA`P~s7zT|fL9m2gj=D%t$6sy z;tW8s%w}%#Oo*X!i(N36o4rV6b6M-Mws_C*NOI7++u}q-5d*SNy&Ky!Uzco{HR@<% zdk+{KWIKgERKmuNp171Nk?1cEXG_Dm#8?t$af`O};Q9HWExyzwR*#NmwSidvUWnum ztIcMI`&;mrT0V4G$qeUG*>XD5GLlRUr8D^!7ifzYu|7aR!UyKMNt!xs%SYu6(GxTy{9O)u$AcZggP4q1$ZPQ09C@O&cqe$%9J;v~nNXmK33j znZe@C9`A|@4wQprf})0;Zqxc?DphE$P1IRwc*GaAO=}V<59CLSK1Y>Xh(swt>*v}W z+Ej2s_2JAb4>=ALOG)-f{JH45C9xQ%n8l9HVkv=YzlaP&o$H7W+;DDGah*#sq;pgU zl69rn6-$?Owp*;!l%l(rF6-{JXs}X{ZS6~!VM1fEkR=UDY^Tg0kZ>U+A=d5kBY!oG z)}_nhahIVLeer?ZFN=4$Jo~ewsjWrz=z>RA7sw>#Nmhy`1=8C(Tn;1E_%4@4stVs8 zlMD`j@-XcHT`a>c{81c5G5V*H*`qdI*yNLi!J{^qvN9mL{29aC6Vlru(%V%UOB6FH zPClLJ0#lAqZa&JG#XLevRP+Ny9~E+sMyhg;$hoPJhpX~MXMuIVFk1+EY?7nYAO(mT z@IaL*Y9x`31lXe_INOrLM`ezYIvSJQVkr#~K`lnp^;j2cQFU!Sg$l;#4WbB=928$ zg(|c`QXugX16UFgjS*PV?(jp1`e(hr8$fbQnNcu2OB9TK971GCE2AGPIfe-@Dmexv z1&9%5Sr%b7g06v*ksn0&FpT`sySUCpBxZQT;k0Wa(hBc)=IEu>qk>vcuyu3pmhG4Ne3vM zx@P(#l%2Jr96qh~cInMB{Q(NhV)DgdAF3lJ)8kA!K!GO`N&}(ZI#x&xxteQ_Jd`gf zTPOr&mmYAc!w2oelpY=s4_U79wO*K6m&&vm7sL_ zpew*z1a%bv0#>wD6{|Z63Z5fqHeCwR?W`(TERxI>s1AfK6_S?INE(ASal@k{AyNgM zeL9;7dh~8lt4M3X_`*h%uxmEpIQwkJ=0hf>gA11XI(3xsN_llhHPa)hh<6 zc4nq|A$}LCHeVPXEew~oYk`q0kgW1zhBI7HPLvKySZfOk3+-iHXjqux=0jXJ)^)_W zwI0L8T2a?P%5s;kg7xx`}H66_qH8)I@f zGl_NBnMo|h`hZs5u}K-t#W^^1m@97A@97Rt|LAB5%J_7qaD>JaO?IRVT{qK4svb@Z zWtb5vwoPYfZE@)0hh;(J1j90nGfh+{Ycw^K!6LbgnTQ~@PBoCIa)3yirYu}N3&=X z+|iIi>uL-LRTW5qq(3W`3IR$B9u!UsUC3bnZY%?7mWW#7o~d~`a@e9k4zxKO+_6$D z4UgtL$q`lB#&Vv;N|7itf_j)jPH`y59=(c|LuKShqSgr{L4+e74(A3&l|2yfVn;j_ z0U>21GXmx8Hb{l#@`y4=4bgB)mNJkcWm9S-jd9%JtXN>eTEu2%8Jn1z zvQ_a2J}9qM@suwj?U-$sW1X&o1tr4E_G*hSNX*934Y3i+cuHslT`87fxxvzqD~fgq z;t^!nro&?>Y2=dQB4_lV1gcn2T6V%kjj^53qlYTEGARo&afg(9$CXlY&nu07=fimv|@v`RW6?# z%-8|M_j5&TkY}Jh_rsTRA(y!cD5M3>p08;J3oD;piC$7P=E`NKR!N+T)ndGLppb#^ zBMch5B;zztn8#YY?QeBz>u|PJ6?6uSMX6T8Sii+!xoFwXv5J`rCDP8BHz*lQQ-h1F z*}S`CHIQgc#G-kX%ris~s?a*s5IlA{;%&+pjIBtjYGlP^Mdx7>Hr}3Sv(sjT#XL-h zQBonKQJF2oC}}br#koi>c6tS_pXvLm<9x!YRZA=q4>KSc*`~oSc9jlCBUC*or$%#B zPO@*xaV^r;rjiubE{xL~lv&u8!ltpCjVi;ECD0})$Eva6bjGU3RmX=>m9-_QqZDmA za50l3ScnlQaYv8T-m{%mNFzIPd>(_mg3r)TV50zxz+4)_0lsplBVV#Oj_QIdCbHY0 z%x8sQdd8+5k0eIM05CSk5DcL0l>u$TN+(N6@j)|16KrS8)E~?=In!aCXbzyIU6e?Y z5d>jq&}U3FQoJ2b93$}R8R=!G?^fKV1SMGWv3)_i>Y5}c+pM)SpoeSKzBr;}LRbuI zzIQEIx`bRAl<0p1WoS4pdq4^NlItsk>s{DVD`ra5^dZgmaY-f*6 z0o{u0D%gj}>WG!V>`?6pa{P2-& zVM*UGV)fckJKt@7El zZM49hPsC!or5|ciW4*?EakI55+Lg(yO_4()y)f@UEbOaCWN=|eFSH%?@?>zZs`A-1 z>g=svD6_g*<&^P3&0R_K=jO9TBEC4cSYtoE~0tW)+y)KcdT!t4X+zD;n2+58~ zbz~V)WXf#c;;o7-F^2PxSOZ@xm~oG-(17ujA%x+n3mj&wVf<7cqY|fQvUp6#WsW-2u$f&XR@u<7gly&> zI2bC}h|R!~bApZ7%!KVS3H2B8nUqkKI1Qu4O9z0E(}LE{zn0bFg&s{3i+C+)tF^6W zw)kL*?6?gv-{8fZXIg~YXkx{-cs`O{&@4S`J^|Un*ye-|#A05M$0n^PI#^S#hs|SC zB|z%5JdaJNY^Hd#7jQkukWXM86kt{JfxxQFMY60{)p}t0lEr~7mx9-MXkQQ%hFt*( z;`*YaGbjve#+D|^K&s1@CMYB77VJgUhGn#~55Y!-j6|lYl<5AP(1gV#+Dc7WB0-~> zAZ=+4sGm(y{jiH-kvPR_Z!HcbR8gn=ks<--I~?##>j%iFpup-72<+4f0x~E)Xtawb z;C+9zfGvM8nA#v5m|t47aam( zdKUO<1=8~%s#ahlCXmfquRwb)MD+@6zy=6`?F@kTYzQhAXwL`D(N%_LgrHTKVzZ1m z5mY+-LRQd+n+%~iY#*_61KT(9)E3k#IF;jB0S+nw{`iDekQE1L{$$dvNGlCIfdM@i znX7Z5E*|t5Ee%nDrAX{GVX+FV)vN)_K@Hm*t~=pa?dX$pV+hj5-UKR_fWD}zs&fT> zQ*%WUHJNJn#!Qi@-5*CNRG+QWpn&8cWjNsAMyTLr$ZxY<1ddek9Cldcj)R4WD?lm# zT*E{bdtJBYNi+PiSlk*C>#cN|S)Z&DEL;C{IsoBLD; zAOO-7p-r<&tD^LTjE9Itd2vd|Ji&i3Cd!N~5Vl0Qp=!z?$K~eu7UkyS^my*AD&N!= zU(05pMYHwL&U)xdM!k$HTQY%@e_B>2DoCQUJxIdxze5^rJV?X036}`zK`MdZuJEC4h400gS zWs%F2`ioHVW(QKE1x#-Wqbccdt`mky1$K0%^25;_SeF1Q6!gHK_FL9N3q&8D;8q1v zl%vI`+!;&y=p@AY z4_{9F0^d{;cbdE>_k*3M+vn3ZEA__Q{QjTdu``QVmf!~qb^{c`!EIQJ=b=}-){-q( zi!QiBqbxiw*tp=C5YK&7m&{m|N5tTQ+k0d}cH5Hl;Gnh5nD~IbW1&a4&hi)`m)(|+ z6wBP%v{^@E7aCbu8!@R4EbY57{{z=CFIv95g&R%SLXqnj`P6U={zM_kgRhV%-M)hs z%58(PJLu|h`z{n9&sPTtis2B8>5Un)BhGO_0{gLn~2XW|8q(iyMaU$w!! zHi7$q+ys{Hjq=phGhDdQ?ewr4#mY)>`C(9^!LUy0>QTKqN`fYSzLv;MyQl(ULD4D6 zsWA103J6@#(#wFBV;CpP!e~Jt4qBTesXYv8fGIMvq=@Fg6h$`NsczXdz`B1#x)maYbJ$9O?mOHk$C+(aKx4f_I4%^@L3L}2|Lbux zm&ZtL78td46Xgokj~cl!6=lg?3?sWNdEL@5dB}_1vM@3Pp=}{dzEB)7)YL@sE{`Z^`hMeu|x(G80;kCl$(-cvNFAOk%y{WvZaH7Yz~rcF0-w+U?W`a zy;_6VWgh52RY*#fru>#sh_59DbxKJlbMsl8+H*`|xg9@*La8E?S1N4eG37{*g^Prej7*dtK~3W1=)9Q-B%O+pY*Z&7n@xy%FjLh7(?J|Z{{GD-|# zwq4#S=kw`d6&%)FiK2G}%MaQHvW|-`Geh0I=C?J1O}| zq_P>@h;@}`&n|&`41?ht=e6}llch4{I3UL*$mLPn(YrWugwv%bdpm1%ZwI0$TQO{U z1QHZO-DnuF43>f4vVc1c;3eo~jkdFSYhXH18Mdtn93-e)6fQy81(7mq5G^Pwg_{qF z;bMXkEk_UrR6w|)fowGkI_PqJjN~OT8O_r!lBKk{m=qMo;)SDx?6!d7Em4z^hNL-4 zdhBW(DqX^1(8LF-kS#dHGKtBNjB*Tl8;LZ;c$Q)MB^AIGvsnO99|Iv^uW|jxPoHqZWQHrL#M<5Pq5ao z${j5>-F^kZijtXv?fM`-=#Esr1Qz+?24BkHe>WpQ%L9*yV8es126}ndr>V9{+U>Fjp*X&EwpuxGAV|bjWfEvJv>;tbdX-I*`P^ zgf1+SeMII=mof~;8PSgP91=;x9vyoem6LQebwSL-+AoZ*90E$U@Qe^TDOA$o94v?u zbw@dvIMz&>I;zN)ScWt>*r#OdG!hLHZ)^xxWL_n`@m39#Q`WILvE5-wz|h(kpq0%N-eu4a0I3BVo}c*lW~! z`NftXM9coD76NE@i5g{N!KJ~=ZS24U<19QdULrhp4dhNZ!c&ohbnI>Ez=NtYDLTSK zr{=$pA^f>d$Hgod_qp>?P_>|^TF+E)DhHT zQm+_pv3cZ@f=FpnkR7ZjJ(^`^4oC+229IW$Ejqxmzz*v!RU=0h*rGAQ_=@Eoi^B(( zsu6+3WdsOxaH$#|uDqZIL#U^Q8b5>w7K5V0>Q!rHPIRdn0%=Q%(7~l@;9XI{0qAIa zGC@&822-Q0wOg!0YwbcBcn8xbqE(BOqf6DuQL7b_5M8PU)(f#cL{D87)B?f9XK=!) za-p8ri_gMm6oT3wAjxR-1;9dArV2uC59l5W(a^l8G6`r^Ng-=!E2u%yBvikIpv2nU zD1Y%8XsT9p!w^Z32bv%BNNzu>l(14MK8LvYj2Z$h+>U}C7Ad)l&mf0tKD(iV5|N>j zkl^)rEW*2s&l=ued=9R_a%K!|@~?CCmJ`1qDj3JiG0%$k{OYT>H~@smv6`#5qy{OV zu))<^LAo6U?T)LKeij8)3eW4h$+Wui+MJ<<*ix3!M0yJJtnwO$o3SO+I82LyL_D|Q z-b4vb6<`E)OLVtr=h|~XpTn@3=_U~4LyAQgVAC2(XGJ^g(#EnAXDno@47LY)Mrsxj z9gRAbq=r%`pJ8_o2ui5=HXbO z*}z3ksF&T^l6}eRJ97-h}{TY6)=Sx(U`2fk^FR~gnJmQ zFyqAnG7u0}yFRVWRV{|dgaSa7GPVieQ=r2zP z2~@(_a0$MKo{zymi1k2rk9MyP^2BLqng7QDz zl!HAdnu+7VfEkEEgD{_50CUv%r5mrz8dy1-xWl^NfZsdd=98E7&*FFSDQ05Gy5Ef7 zJK_F-|?zw$IQ@gw{){_i6<2p1{B3Z1@qlVP0Y47VCZs+S%3(Z8z`u`YiKJ}ci@qYsM9LUou>;8AR zcQ?XBsttb-ZkiTlKS_EUAty9r-(%e;!+qmSGjW;?e>U8Sxk%r-m%{zEmz#-?*znzO z-wwCSuNUrnK`+NZ(z_P!UeNnG8{YV5VzFO^sU0m&_N;VI10KPak8GTT2-)G7 zvc;o_Xy-=Ve&6DjVZ|BurI*CUFouC8Y1R3#h z#7O2#RkJS;o;R`&V|8HoDUKQL>v=VLJ}vPIH*y=raP!nE^z7!MZozWLB&#}JuhO+_ z(d|5L&Dl(3T~!m7idw(*j=Z6c^wT@9!giWxr?eFhN!YHLxP=1hL88~*OBk<6N6Jjr zr|6a9EH3t*WKUK1Mjf$SMf&iV$*QW|>J>egpl`q}R3l_l(G4a9DroLx%dR&WE?RH_ zqCIxc4I9Z;AM}HuR~MtGP^^8i53IYhS}8JNWt!Z2)kK8Pvf)jt3$_wS4R_)snk)`T zI<~2dY^)orX!Zs5djdkJ+qBm`>Dv8Sb<1Y1fFjLkQ}yqEh64}UZ0|ijGK>0C3)2j5 zVt$1GqJ?RO_uUvs8JA{wKm772%ufQi2ZOsy|J?!n4*`4$1{fFSo&bK*!Y970!TbP| zT^FVec@sVp1IyWXYo74WisAL%_rDTH(kdxnSQ`D<;S@LziLx{XBdug{Al_BER(Mn zvCs4HIf1ExbIof!{=C36-;&^2`lW{BEI*p>yI(=TvA_gY8w_0O*Nau#KYF;+{KUg6 z43<&y(Zs*1B=LJzMtocn0_D@@75Ys}3azS7+8;kD-XJiOXO z=G`^~rmn6v5Bo41%{M)~$^4CnuQ&TW{5tb<55K{jFvHRFMzhevZ!%|kIAJ*NQks+I z3J>?2bskQc>pYw`{T|+8-t6J6<~9$Hn0q}uYB)dF^z-I(9=_2$?ttnu*e<^~V%G8w?zAh*tx z;vE93DaGFtSWPMJ5%^3oQ}8i?o5f7QX9Qj(CbpQ@1pb_y?*%+)^Fp%{_=^a?*xYG8 zIEOQC%GjOqtMFZzQR&xf?(s%P7#GdgJchW{e=LBX2;e5jZO|TtZ_V5x~U&et!UeK7c7tF78hQnDXK9oUb}~IDqdC;7FojF5J%m-3lDhsvs)NMyd4)FPm?nNb@MJfyvhTJdWaRrJA^O6Nkq8i93IX(geMV! zmUDPO(9ClP^_6NdW^rz^dFgHW&G4+s=Q#bGO9M)y0ZEJP&E;yN8H}8U0 zb(qUVcO&aatPWjT&JfGdF_}^~?@~AKf>jb$9$50|^MiHsE_L%Rb@MKD^Dep4a5DQG zQz*ih3 znHKFv>*ihL2|Jv%#^yM#nNz!rA8MWJ=3R$vtR{Gj% z>*igs$A)Kk@O-Ym_E0zPBHb8nhOir}n|I;M7TOPMm)&AZ4JHMgMgJU*WE ziA<*iJIm@7nLly6%h)W27asOm<*m%P4QA}Hs@FBn%8c7#_C9sE@eEd zhGtPW?_#a<;Ke)l7@JPlHb#bMcqk6Ms+)JQuh`YiyU@6Uib>tP%hI0Kp1OG#Zrsyc zi$2Fmt1K@r^DdA9)>pPQXPjIPUfw1yl`s1+E-(9Zpv%fS8Jf?Z04?^4UCMCdu|cU8@IXo- z=F*82p>l6;T33zbiX>mEq!)A$%z_V}M_B+bXqyAyzja__oCDFEQ38PSiaDOX#7>ax zfZ$o4Lt18mC<)CtF#fzOv$(flJj)`1k*6Qku8b85h^7|`Ms!cI_{iQO3(ELh7FoE1 z!3XJ8Cb^LzkKRC@s4T7)sE8mfo0wp*1UV9+s=(_)kztX@#6TXlzJ=vu4#4>pLF5N( zUwzLHriCgkrr2`Gm$n9kiU|UkM6oEmM8ntg_0i+fPy}k_q4niUtqyx+PKo1$X8oyj zZ-eDmKdPyZX;wX>`D;9;Y4AXcJ~hdwDaLU@e;mKx;Tw_mb655k!Hw4#F8ixM1e#|`Ip4~RJT)Mqk7y7-JFpVNtO&rU9eJEG#* zN!-vkGd}$ZiUGc7C$Gi-5mnDl4#EBNYc@VfU;2X${$u*vy-T01yuHR<8~%4|{P)zj zKMMCrGnHSY{F62QpQ~|ysmA@)8uwqqod^HP*%MIm@8CBZxc>_7JK-j0>HjzQeGlsI z68!R+ynnL2)y%^!iSNfj~_fU}k zd2sWIBt8d2{}|lkfqw_wZ)iYz_@)1)_`NCc=QEXakUlGr{#U_$ruj_%67+@XPQw;kOjz_ZGPKp}c%-hW>oU@-2b?-EiL? zxbK1c(OFIMOcTR@5biG_ev6IIXD#0rgf}DQY-yPD!{s=nY}ReQ`nt8Nues*B#D-OC z`VytJbW&d@A~it5>brWG=t5@A6k| z+Puz)(U;|$%*E_Wleu&=jr(0{F77W6XG<3j=Ps>&2m!Me4CX1=mnx6s-K<46g2ft! z8v2MH3>-}Ji8s!c3X+aadW|2VYv)mR+9{Ji_Yxo?hqE~biDnZRiHB??J^@%75eKG< zCXvwTuNt{=PwI^%hjV_GCG_iyd6*AKOg|mv9Du`o^%HfZMJKWFE1?cwY9CezJ!)Vn z0plHQF*hMzre-B+h_Sx?Tf}4WFtJnL{>4lgx*HmS-!@uEV@XWi9@oT}sMoiD+Y{0D zHZ&T_7G)Xsx34Y3QYcpE!Zh4&R&U+@21on6F=edTMQKVyHpPxOtpC9#H{QhOavmGt zfsu$<2W>>uw|`+t7~NQgcHqk+!YF6+Sh08wHJ64hOK=b#?JzKm;7$!0Fy3mbdFj5` z*LGN;k_rvIz^b!g@nUO)ZHtRVluSbP86p@+m`0v)g1f%`>yB^Lw|{9#PQ*hjD~r_R z@p!VBk)b#e4XLSb|FYq@4k6e9=Sd{+i7#8ALnsc3U3=vy8l0T|0j!CbSqodNJ_Dauxr?G1od%Tz>5j*bwqAL+XTYYa-W zoVL=D5;~!(JlZku3&mmkhz?lth6ggNZ~xY}f7^RtkseRP`>v(F{i`<0(lCN9Olijy zuG|GXzqIVGZCyIw`u1)XHfD0O}Nx4!)=ThaCHUsy(gSvpK2u`ev6hXtQw zx7F>f;=Dk8`&Tx;>)XFC3QO7b?O(s)$?dxO_HTXrx4!*b-~O#{|JJvEUo6|qqO6pS zi12|AYm&YvI=qn3qooPC2rMUH<;e@2T^udr(B$@!{?Tj#rjN53Zk5W}SlpDQEo3ou zFVBXkmaafB4RTTpQ7Wv-s7c{y^p*Cv^oOm+ZAIAP^} zH0Mc>^Jh@LY8LWfyFC z4~~wu^u7Q?7uC{Kf7!*ofn3cOh*$k(7hXG#xA$kaV!IQkI&j7a=Oknjk;^9sGxqLA zwB;%@4ZZ1sElkcM1_~K$%;FXb_Hl6^qW-cAkFekXj2v01AML|qT*>;;K6?jl6 z>fxx0V2e}2*N^te5uf_eJ~>B$!K?nVOM6fFfQ_8t(*vRPqkYxq*`f}rezXrq5bH<# zmUZ>OIuuTc+r!{Iz^#*EpvRKg`q94n(LQR3(@p~*z$MY%t7U7R*UYWo11pEbIfbtjm`78pT7zo4he2e>Bddq+alVE!D zaEISr{CDSK;x*y!1&ysPvB0kfF!OZZqX_qZco*D$E-#Q?74FSzjOlfO@aw|yH+nt* zPbaL!a|uB5xrF2JNBWS()aMhfn*KQk1fBAc-~8P33FND~HRs43mW<&Y7hU)lett${ zy7+T^xryO(2dqQ)x#tx^_epSE4|-V_yzYJ`em{rue8jqW?VaUYW!-#!f#snroWL5O z{6pBQB;U7^+BT+cX^;XUk@|O3IY6VF*uih^m^-<*_veAi^_We)|HLZWn&ViBXul z0yqrwcZa}tFNorX;qN{Key_!!__7AmhR$}Jk?_54A>oS!9u-(X^FD#u+Zyxv0RP~r z3}4hh_}`U<2nRobFs|$^s2jp(u52)G2RzPz_+EpFg5&>offfH_ftCNW&xpd`D6sPX zeF6S~S1`<#68Y~1)^ygqlK5-IkrY_*4H#I)&Fj^H{~gU|0#7=BhN9&qj0X8Tfm7o6 z&j6omW;!E+-zl(wX3iqwZx+WZ1y=m41s+!i{&)Ej;ouR3Aq@+`r!NO_&p3-|wlJuC zE_@t#hTpc*n73P)0lsJ9`!VRAdp6@)`bgMm;_)FlS)KJQCm& zijYjTu*&3R0&f!h8wB1g@J#~0M&S1f zoD;YS>>IZag57cf;d2}4@hVJw$K~-OV_tb7;nxa&5t`7r;Uh)HTnNz_H}4eqn*!e} z@F-Y6Zk`bMeu4j1;8SA6R|HN9{I3FkMc`iw+!|+?V`eb@4+`8S@Q(!U5_kzjWZVo3 ze7nG-0`C);G4;F80r+)rCz|?VbCS&Y=-mt0eZ6L(pV#-|fvJcy-$?eT54*J0dtv%}*% z%)33j!rbfOF7rXaMJfB421TEIv%rhY!GI5LO|=(c8J-{c@Dcp|j_{#J=I6xsnjZeh z*Nd^k@vs+hs7b)$5zS#7kZ*Sy#h{66w(4(0$X_pTFk4M>=C z{*NMzgEs=Ft<;z#;Box%+l{|*0{H$2f8zx3eHVY@1n}|6v2lTnSsTEA5WxEa_+&`0 z3*Q&Olxl~+H-JAAz~2nue+}UCW=3)Q0yq=EcLeaC1~B<>o28#WY5b8tlu-wNqRuO_ z#*h!w@ye{Z{PK&Mm#kU8d1-Uoa&%0;%5B$~hx2$R&m0e@GQ}3GR$9zpDiu#a;gTQD zX41>?Xep0AQ}J_2L4JBJjK|tnw0E_2ws-Y-a7B+1Wf6noULJ$SsSAw7;gcQB4K}-k zaKM1RZA%ZCP&rq`Lmip4kAh>)jM9z-!w;^u75=l?Oo@!M&y3vhd?Hs7iep$S@%Tj# zV|OBUEX>=(J*}RC5U|upf>TJssVtxJiYau(!gM^`WAGGCq2zgjmpN8KV=vToftPLZ z6`fr@{2vV0pi}b2Ge15~H}dW2?C5CgKyYQZuRy%xi-28{HsGEM`Tmd8q)##m3&HSV zV@x#Ofg}g8P_}|&#Q}cp@HOetGEd8kOj%igR~Nm6TcN+Lc=*TS3_!8WW^VFK;A>LF zu0kf6)hBcHl-%$LH!&!(R%IVW49G(DZfw(hU9w@;sH2VTJz#K_e zLS7mi&S66h2ltA$^x*mVpan|M#Ol$}tdyJYERkmyB6)K-EfGI-VTQjUKJU2a~1DQKMX5@IH#E7fVNrE$ly% z*`t*@Ul}4>%DNqa=jdfLVgtvvL>b%K=oithfJRpXH130!@lK1y@jx5L4&c~+H6Sm} za7-|H-ocT34~WjU=@iSWIu47DLa}3ESiX=XVaLL-=nxc(?7Lh7d|gd?0HMmG*~bPsoWFOHD6ip9&er&QIz}9!nR#q^tZ{ zN=C%His{Ul%PP)N+Vlp8u~BVnoMp7=0LvmeY;M#CW&sub*1oH+XlTG>+I}`S-6oxcelMa!23f?TsgZw1c&zAu@Bc zad!6|a5PTM^5I!5&HP8CaM)LwsQe4oyc`094HgG@7trb`g#B<$1_@%tAA|JR` zcpFqo4`RLL)(vwSBN*OZSMjC*|D^y9!<;&6`nY37;_*N{o9X({}5O;6vtr$YFr*jhrUDL%Ow0o0X9zmE%m;#tiHWEMizW?Jm@gm>< z;fv9JhcC|UExTP*uJUmETiCwO(e{Wdomd62F<`>Pp=l(931QDGAMHl9VW&%y$1Yg` zySzTM-RVMT*jO&!?D1}^3p-%tAemsRE8J;o)BQZAQiaytJnY%=u7giock+<(0Pb&j ztjaC61WR}s1~=GbH?>VSE7>-*C-{qBMlRYCA^&&6KmGRj!xW2#Dvj|KP98pT zq6-WR$UBr?A;t?-h*``7Em6@AL{TB*ZSK7=V}>11ILwHO`Ois5}Q?W*JGHjg-qQY|T- zWkHrlA!&4l2^@{1R3=KJTQfQI;e3q<<-rdnEY{kt2D^yWe$|Qw0zitKeMKCX1mag5 zw#2cx?rs`k#LF}g>aNySJP&!4ij_|misV``F)*CTrqO2NI#_hIvP*{p#dFkyFr3Fz z!ayD9a)p$~d0j9zq5A#5qutBCc+=mEL(j#Q5=~eBx-WUU)w2CP%x3KlNxD>FH2Vmr z$S0zY4y{MzjVw5SLW+JLTK!I5j%DzYeB06IFTzfh|fhQ8m zm$GAp)R1Fed*q>fN%_|*mUSvaT+j|5v@L07a^2}+j_x&pjK(7_<6J{;(1?T&)#+kx41DG02u2D-qpNHSNTI*@Rw zfcKkS*)V7mH$2KmojpTpt_{~ zBmo~*lHvMQqS$1#4Py0*`D;5f)4UMB3ss93NJb08rR_dhAX(+b3}?71on5--!&+NV zSZFWn!aI4G;pRhJHyR6ZI5+CU#adC0!0IEY_;x&hnKlTU&vSp<23nQrV=F z3`KSUS>0S>F>MLC!PWXWcM^Yg+Q$;2q;IQl-ihy($BbgB#&ff+j@hSpJ53%Ju zwwtuk0b7iAk0$Ev#hhr?Vos(i_D(+? zqIG44!88P2WmnJGa5`hv`4L0S9NCB7U+M*S9-Tp(l zCCRIlb^8yPcA`h5$Y9^JFFSzil$#AxE*HivuP-qwSMjjrILXJ0}|!-D0B0k5sh+#G%6b--V_xM zBa7dTRyynUAGlrTbYi?vjM`yk6KmCC3A<>~r+}SWEK#@rKrNwNRb{ZwE)}4$0h2gw z#3~ya7TUaU4DC{e3N~Uhuv97vHexdqZmZjWsM~+Atq|f-xBrkXB(r#JL4<;49<+BV z*6lw?R{;^JVFj&()a^gi?LT13!|@!mEo?Rv61qHa1C*f z{u?vdq|VpmP#nUvyGUNu(W9*m8S1k*7KU5kzAEwRW0Eegxy9^iWLzA6J|xFV1wCm+rzunUMv; zmMAw&Z_6OZ*gW!uUb^8xiD1HNfScebKFy`+X&BO(0_;f!8_y1X9 zCh(k%xW5ZG-t3;>>%#Q^N4UQ_(@dOZ!~YEKy>rb(!n*$x?ms-yOnk(KpM%ci4REir z?gel!=rj|2+n6>&PKA5@sb=DJ)*r?~3OGswt06RloiaF92OakER_U$N#(Z_nGsS`M(hR+00S#MsS%cffP~@n+PYXkBZaUL@KYQ! zBZW+s*$&_WZX%z?yKzM`CLWTH87t=)v6#;egBk_~Nr~aYjnF$uDU~rgywMtwO4n^CFVvVs7W8B4a^zk)SA0FrU3mRUz>N%pD^oEqWa;JxjEVvYg zwV4FDWG>b+C_RBelt<4p)<&{bgeMS-jtf`)uHxt(OD`v*;)QfATt=g3kyHYPkJQIuREHD4kxWI7_8l_ws9>4|_Myl5jw z@^h=Nd_rD;gt7GQKxLVS2xetD05oTioUCFU;q^}zZMBx({-{G^@*1oNS!@wK-?SD~{upxjaoINi=toX#MDw0cp+y5l+av=dYG7oCRu;udI-&SfU1h)9CB z!=#|d%bgz`!c(<5Ov*J~YHT&Eu7wqA72`+&BIfi@)@!Q%$$IEPI=A2qSimJ9!o;}* z>}*G(o&|mZ2c+i#k<&0ir~BlxCCk^KuBw4`YkXt|1+!H~1Mk@oR4mY*51OOQtAX~6 z5VW4|1G<_MOo=NNB?n6IsywFEKamAo%Ho%|2BDSU%{4hT2$Yuwe|<-hmJ{frg1WTQ zz#BkRU?3z80bnU1Mn6YOLp(sGNL&G_QUw8vwHA&ya?8}$)%Fpr*5M`h7WvCHa=bmH zFRH5QTtVN|T(Kqz?T?Zal&{wQD9}(TsM-Zt`2v!+l;NO3K^tQ!2F?IlozKvQqhnGFZ!oyQ3>@=@M*V=wnqO1AKxZiBGM~N zr+;Dw)IagJR{unt)ni5aCq=68I{lO4wC%g5YxP_7vMr;7EhE%#!Os&~m4Wnc()|ZH z5b3hWWlH_UbRv@-NI_GR$QMRa(&6Cr67mxTVn-)c6l1trGdv0%rEWp1QjQ+@(`Icw zv_SOX3j%t8Daz5}Q|^v0eF6oE(?2ou7o>mE2#3-?S>NgPPu8=pe{xkjMgK$&{c8PF zDKSvarO=1VDRKOdR&YLkAhZKKriL>aa?BTxxsQs8QbCF48KT3>neY+$B0Q2D&Rui` z&i4}TAIXWKQY0}V;+aRfXawgkX_J@1i#j*1_y50nefE*080XXTZk+_#QJISbM&wZi z2CFVe8bwn?h|jf$OBf$nOg>Q95FpFxA;b{Eij0P;T!;+2xK1W{U|tr4SSfQ|C<`~A zW<(H%3n#&rAV(qz;)7MVXC*Bl&nFQSN?Ksd0k}V~qQwriCgkCU3Kwd}(Vy zXb0LTP$L!vkmh|^41hxsSc+z=MK6rsopYK*Ht2o^?lS26l=TP7{9;Cv*yN)>?Y!+l z_#3SMiEuAQc=DD0XTyDUkpFpbpK(l+83fbmAA>s`_;4?pNTK;cvq4YlHmW z0{1?Y_vJSH9dO?e_}>k8HgMkq_a|mGnNb`5gK&Qd@wvys^gj;wP!K-4S+*RvVX zn5Uq9FOTG9nqdcb{7j=UA53#X!B~Z0)@$-^jHZvOrSC z?r1u}0oq2QO1v~8j#R#E5((XMuu5yIZ{~d3V9sJyv~E^{m|G<#R(>Vi_syh%f$F%_ zS~Ve(Acc&o-%BOb;fwL(Lx9fW})S#X)`=6(82j4bb3JOidRxqoni^AV=7_=rAuI$ASyyv+l9=u*Yu|3<;dYN z9?8^TdCn(@N?3qRpP}JF#}y?;85P7(JP!R=;-d&h_^ zSWm!!f1W;1J=!3d2TF0{g%!d*Cvp&X@5#Wp)&riNlf!J-`N-vygBc#L!7>hsTa8=h zCfqneBw2DvWUmyH8$;ha6Ob_?qSEWGQKu<1GXRVL;AhFz%@nVW0mf z&3PoIWHKM-85oZ73yH8F=h6HY;g4vp6=;Br_R_-7g`<@vuC2mwJR{*g7j794VG>O5 zwXiY7c{x9ZV?0_?3d21KWGis)bACNQFi-a_fDNHPXW{JU!h!UvaNk-&yV23-!tpm+ zH^El*`8Y!bB+pPG=|i5W`Yg;zspTb2)1{}b>i@}aes0~UhTZ7aoFjKwGWt6%y7(~Y z6?`*27o9(hWCh<$si6ol6Qv62E2`Vu*2H;JG01=-2(8pJj)$f8FHk@m}N{ z_%Y7!nLLP`oHXWZu!ibCq9!MQylz9Y`(~T{rLVVs9m5uXzuowCG-?}vtH~gZJq44K zq!`o3e^uYc^^!z&{PnYTz6tUBq3#;R)CoFhb4t<2_VJ<5x?+=3P#yo4S-1QE@l%MM zGmHr3AoF#f&HtM9Hg9|)L|9i)L^5ZkikGCR!O~IJ2OcQ@olt4B87tHQEvC;e(M@fDBhh9khj=3FO z4d#&ce~p=FN5MWj6CE+WY3=_a47?_8S!WFYWTu39%&+>yeDA_a?nJ1IpPn~ao?ZDo zqU`)ilep$pnz6Z{uDkjUZm6ulRFuvGlVye&+8Z|$l?9k2X_%pJp8`X^{g_n2y{qrQ z&ZlP&nKm;qSw2wtq6^hHSw6UB#t@agmFwo)p!=B`V^5a%!ygg0n95VIvAXjs%|gn- z%0_WD%|Gi2B#U4}-?|E!zjwK$un+c&Q5M)C{_uu%X2RM0eWdRoIRNx-Lwa@`Fr_nL z6L|OLdAm)|jKYk4C%^@}#Kf(fhamg@X6KHBrgREJ01e#gdAm2mpTNEo1JarnP|qI^oN z*Z+mdV@5>=VA1}4r`Is#sSZf;BYpcR&%68f*OV5VJ!cY?`1Xm?JR9h#`%pkF!OBL* z^d3nc4A>S6OS60KT?-n(MHZ%ScK3tRRmfjRt*!M5Ts95Ijt@Du0mX-qQJN`uf1_mw zb)d`aJ9bK0XuW!8mno&Kn4l(rlDeNERo}F0#%>s%-V7!!So-(_m3g~XHHyc=U8`pc zKOwO5Dr>Y7z~mDmWa$^eUT4A@xgStYI=j>hQT(31CJ4$b#%Cj3fYzn-+Zvp`p|TON z&=%f4G5>9x?^*srV)yUfykF#S_d0+O+NOv^cr0)vqFK$ZaEhmLpdwCEr5rj$QxAEPAs4alShzTG4Jm@0Nzj&ou>U@r_%|mIgH8N(M7UlIS8u9*}JN8Q>meTr0yk8peerd$}Z6ki2 zw5oZ#SI+KPJ-d9hR2iz3&BzH}PgA*F>Xt1WUNcuWqH?AB=0(i^)X!}jIk>BD-d(6c zBSgFv?SU-U^ludIGm$9${Yd}%-Rsa-pkhFrP2V}$r%<}fr&6ZNb6uCRZ;tPBgp~ae zDT|d9Ap%Xpb#xHj$vngz+0347wZYNwe_zE_=A4rlbhzpy^=cLhWOG%Mj%2{!5q| zRkj#;mn=XcWs4m#I*xt1p zI1Hf6#V~NtcDb$tQ8&tTP}s?SAY|uND(LDR%9F6uc4GlMPqX1UfXqfOVh0jQf2Hj@ zBIX~X%=f+Y!N}{v{}g5&x)bY?o};O<%niOz?A-ALBnaRlw6{aIS$}_wBo&nk2LiQm7-&GUXmt=Gr0wjZ7eS4M4`FC-f`PLmzll)s| z?7L-m-_xF+XB@qy*M?<&9AQ?T!w4X3=49XFyEi|+i`D(~%3Xbr?cV&@uFa3`-0`T{ ze(bG%k22WqzDFN8FjoZjzyo{H^dDRL;I7Bv-Z$&6z9%QmU42hany0p+?v$wVjD6)t z`kp4!?&_<+>krwecI?HKKeA!QWO;Ap_u&Z!X{S8!A0jfl`hGwT%~N)BtoXssM`!M8 zoQpwHCShAR=&<&GP`O^>FW69d_7#%QQ{Bh|)ksC%Epx0W?Th*Rmb&~T|0gP^svFJY zfxRbVoPXd)^X8yP-`(B!UDwQa?wDuFLpoP_!1nC7?l?e>y!C7}Nzdnl;ImW<+CIvD ztn_&MkW_UL5uMV@kOYY=&De)}mVy4Me8?QFqNitHZPJ7Xs2-fz_TZ)G0JDd>4r;69{{ z*s^zt^a2Yi=R&0^Ev2#(jgTdHQhI@tTnn2lS1MbzSC;mAFz5v;m1}E!eEjbrM@u4l z4oq9np#Mm?GB4^G&^qDrJ%eqe&rGe^4V8-ac-jg;0?IFh?i*#3b9|QZMr8KZzS&$i z+|@VlR=Eo-l8DY=y0q`qwC}>y)jYKx#kXQxsT9BUwS|KNBli%;&{@ubM&UXjI%BSt=1`8%D~Oc{}$tbDb`2aMGCWTBrL9#BR;23*xB z^>o;E2Sd$05hJ6no&L70*SK8mP~&rbhs(jqH^xEVyS@>jqmhdvg(UZ~zNTG`FSD{b zZFYNgF63E@8Rk>xMI!rTKoAV7b0kV|(+g)Bn$t(S$8U29Z6>jsS3J9l7)=0h#<%Ms4y-lLqUO6M5xT-$I> zBl-Yz#yVo}-0@6M_a%{->PFBT$-^_1Z|Qtziab11`CN^UubF1Vuc~{gnQ5jOUkCGS zBZ?|3sQXPWhbJ-eqEqrYd|9}TX6ov`O1hqO&9sEIYE;WaixhxCk}H=%U%-5ZtxYRK zE=7RumTQeDawu zFsY?qK{Al(Z{50MHnO^)+|MPK*|~Q%ln1Yt<<~;gcja1ywUT=?$dOJ-aaG#Fjaq@A*tXzx?t!`-N(-<8w<-s-yI`0mi-mCl( z9Fjrf7WC*S$cD;2HK=DcRNh*Hs%)raYfyVPRIZOu$80e}cQSnpS4@BC4ukeL6#-q`~$>-*)vuGO=2^$JJ>G$Fw4SjsXm;DxuZKGr5k z0VgH5?tl{U&V&|hL*-K{==Y>{&*EHf13a(4`j_ey9=obqb6MPY4xP!vq4;6O8n z;zD%{GOezpDw%mZCzw(du1RoQ>WO-McIBr!!Tj1o+DAi;XzNLs4aTvP;}P-7gzH7! zdT+9$MEsvj#ot8yyQbpbM*K}v@wXFy!&LlRh`(|wew_GL#YgK1JJbCTsePoeY32=n zVrN%|Sc9+BC69FZSQ1g?fKgV5;sbDT!s`|cCy^|S47xUx;c4IJm>n`^Fr5?l>{c?n z3aS&H&^2)|IkY%-_Z@(e`c<;OX*}&2~W8TFdq{y-GP;6ptd$Jhmag8Jc=<|%Uf8O+DNdBVHgSKrp zD)1_;z*#(M<7<(uyW}}pN6GUvYE^y`g>RZZ*KrnL|7FvA=9M!OTF7T6zjbKNP=0-` z6)Y2VR1%TdHGwHMMlY_u$7SpSAoHXqv_YAKLmsi$)MQG~hC;bN8)^y6rk_DLv#*tc zh)>tgkfl=rK{43yAbw}|9k|2XK10^5`+f*<)G3bZ_ci@+(bI$k7J##oz#V<_7#E9& zeLq56bPZ-rq1@lhd#lZIZ*>uKHhb{*IQ~!*W+nbM;4hEAas0gte>dPyzw3rF&FeFz zZLsCgyb9K7M{t(B(A>JbyK8w{7gE3c@{5|6tXaQ#X>;51wj=L$XXauvKYfWYSVx*P z{&>%YqqT!y4ftaG@yp&j;|g1Nf%_d;!GOry0P*0ep7=e=2}Mnz;>s7vsF%!@v{fcU)rx z(|sTR!`C0+Cjyv$F3c-1{c!1o_?r)bf8r4M_G6|`=N^mie$<$s03J6SOO2U@Ma#Is z9x%Ss1zsp{i@?+^;*6ufdLZ(30-quHyui>};(Ld{uN3(G0yhi%zXe_-@Ye-COW?m3 z_-ujyOW?%&BoFnl00-r1JDuK@v_;mtP4+`a@z^Dj(cMH5s;ExH+t`6OZz}U0K z_alK@1pZHf*&rd4bC@ru9>$z2Fi(%7qZAm;4Buvfxhga!Cvb zFq$mBZwTBi@RI^xB=FA#)|PYv){x_Jo6MLc0$(O!E){r{z}E@9MqvKuqh)%UHf+NPH3#h^u{ zl`1v=-|x)w%ni`j?^V8kzuJMD`+LruXO=TFXU;5lhH*`VxK;uuOjJMi@HP+A+op3e z6bj$9&eHj8@eLl&dj>B?eV+*%HG$o9)>-~?(03N*BZfZW5Nu5u%y-AwS6y6JgU)w5 z7bvwwcr;7nzU0?+?{dMK{;}Zk@~}UWlF(#zrC?nLNU~|$6}}DpHqC$Wu+LwN4I-B3 zY7hH7f7%6o%8=+?pZ}U&&^H?T{(A?i0Lxhkz65s;)Iq{%`WC^Oev4pTCm-?X&p>4) zWJ{6e+nF3*1&_^F4$6jjSV^EdfD`&Pbr#f(mH!IC+GCSoE&p_=A6f^t1E>{@V191fA)8zXdu+cr?qO1Xs$#KRkMK z2E$n*^eY4lRQo^`Ce#J;Fha1VpD8$@AMn@4OM=Dcfrn`r3OTL!-pv@nFttwe`SU?1 z|FRNTe+JY4w+251{dY7}a6-)>Ssg2QN8Lidb^HT2UwAZrK(N-yKlkXBnG7>WGvTj& z_X5yY;rS`3+@A@LW+{TYPpG5iVV+=3zf3TgjkDq02s*>D&eD4feKzXjBsATGVfeaW zR2<_h|2?2HoV%LvyIha|4P+^yz9`%u3Dz$bjwXG9Jj@lW>CXs8AjaA7{{T9}|K5ZB z)HtY~gv=-^HAAqjs}+I^<;@EPPZIn!!IK5wFZfu&zYsiCa8mGe!7+$0q2hv%5IkFO zvEaFaPZNBS;5NZ03%*8hnc(jU=A4sKzYyFYcm}%Fgt|oV1LKH~4EV4TO%eU3;8Rcq z6Y6xqr=i#qioY0EsxpiCD#87-iEj}6Ex|7fep~SG1gF zIH3l{82*0=&JjEmU2;Md3O+}0o#4j>HwqpO(I@z#aqBF=x5f1+l7xa%gu4njQwmH37cRg^vxV8yKdy zxJ-i|ku;2u-(x=myjEp!QdRw0@NoIP3ttG;2!4qR)ksd}7<{C$^2jqrp*RhFtb%9^ z9;bBeBhPrFHHjyvQyov1(~cp2VN>box$?VkK8E-Od84BjxwaZY3RgJ#6!~5Gn<9si zB9p%f&Pv%hdWU4(cGKhQ?vp8`n?{l;8Ao^|)p|FdfG=7BBE{mxnoiu{<2JSj=&X#T=Vh%<%{@$0M{| ziD|piMzuV&OD(1?YBB9kV%ndyCoP?Jqs6oviD@^|KD2b&gv7K7Y5Nh=_M<&!d1#MW zOdHH%+F%yb?y{J+mc_KMET&CGOq+@}kEPS*A*RhkyMmZ@1^ahm_V4VwiI`&PzS|0X;#Ow#z#}TuSV}C@<{wNz|ory6Z>pWd(#)BFk=5$f04u|&A zj(Q9t7Z>7JTcN`lO7|EHXE-K7EzeQvVuz1b*E>AUoz)SliRu|g&sD#7I8VLraK1_t zt`ypNZ7*>B{TZ0~aFOo1v3tlXF$roJj^{q<~igqD>2m#d~E<_J>puNDUS0Uuy3`GPFFtfkGx2Kh zXPEKwR|P|98CQjGp5+2HDSaRDt>Cfr&vQRWIjn+QVmbfm2iafH{k!&q-$q%pA7uUi z5%5}Q7TVRn5PX2wuB|p<`#s3DyHuP12+w}bP$d)`9Q(vM8Ul~<`pP5CXp%gmrQiG= z;$?rtF~S>yho;!WetWMsVY0mwzYG3Mb&Q4UC^|iDTZ0j{$$CnyQ||`B5d3_hTjRa|9nJ<#?vx z64xh&;J1!_v<;tq^uES}ee?*2XPQ|G=5e+e%@ZH*jg{t@nFG@2$_$V$$7#FFOQQ*& zc_FSvI<4NOR~0@5IQPry>O$ZP!9zOn=UG=%4%F4dCj3WN|GVqzRVeFBXeieCYk}8d zz9rIchoTl}$GMlNEsyl{Tc)SeM!B;#Ld^3=e8m`biwl3EddT4{^|-@1 z>LrKs)Ef@xtG_s0ptd<&s0JE67DB@gbNDzjyG9*SqQ*J;G&SAf8HyV^=|VMAaXYZa zv(##bXRGTSo~G_~xKusi@JZ?!hv%zbIDE2t#o;s5R)^12A36L*wQtPEd$tLv05CM!~~nOv*HL;bZH0H!#nIq+>rq zycztNIN|XYoH0ok0$UfVHQu=F8aJcKcMrkmW4vqopnWkRmZ9|6AyY6WHsB@*K0xp! z!P-WO3qDBbrGhaLVI2FcA0a(wflmA=@SPt1Jojak!z#$68DhN8V2bVYF#pDUuHzrL zftdWxgdNVhF&ua;tTF2Oaf0^~TW*Hn{XJVw*Gaw~ssmiR;Jj8kiEx~&=Nt$f?e-BG z0%thun6^4ZFOKwi4v%y*B_UFG7U(A-4GF}>wtSZ01EnpWC-@*~%Y3~HAHSXQT{^b4 za|z(s77oJ(DBCWGFF-h!{&{W}lmpwvAQ-@G7wai7xX(koXh+`J7FZv?32fWKor3>Q z+rrS4ws5!>BkKQ9Z(MVzH^+OJjB)&WuIsk0=X|{jAHQzrlL*JUZ87Wce5611blgvK zMjtoV>0@>$sD+i4+2uIIt_e4FRgK3H_&i*YXX;I9mapP**;%;-xrI6TxrN0J7U1$z z++ZfGIK7Xo*<*?)7H4I{ho>NA*nn`7BYktm>@uLX23}iIUFBZEIgflL7XkVXPdNq7 znP_%+8K*+><-AYwa!NvR&|?X1(kkZL`FK0akK5tmi7o{`FsYFMcSsUWi*z9u?2xW3 zKOBdP6)ucxZgn!jS&t<+s#H=}2wCQ2;~L#!{)NCb>kjeaBuMv85#lY*&&$in1G^Tt zD?pz7Skh7`VFMSBSQX8sSfbVg|Dh=Y~x|RQm0H^ zfL9^CL|VSTDR}s2Ws`x`vcCF!Cj{Jg#rvtMc`Y}qs6O|zZb`*L^Q;dVjb8MUh2ou` zqvN%~`cb2d=I0hez+SXFqz{EKKd(4j@>O0g$5!Je$fXsy^sL3?9x^`~X~V&i<2IlxbTmcT3pd z@qim3nP7bFuF$bXxS?@(15)1W5Mg8syore-rENya$B^nTK zxzY#3;(XV(a#41gtj~=tB2{-cVCBid>^neX`_+s**IVw8ky|R|U}L#c#!`7)uBGz8 zU*TOU$1k&8gBSY2P<>vWz$?6nzR(81v!+D@NU9uJ&eug&kO^?(9AFc&91e}E{Y)-A z50^dg=VUv6okNjDs)x=cvJP^7uH%Q&M0#*Pj-4oSkeZf)r(E~S3S*WsjVOlb8 zoMb@Ab5oQWx$lv|LrTqP<&N9r6x_I9pydKbw^@Y*aP7S#ds+1*oukW>p`?&?X`;+{ zCaViXmn1^2!HLL@s_QMPL~D6POI?*N-5gzUk?K#=x$LqZz;VgcNc_S<)jZA_op~Ow z>qN2Rd}ovwuBep5&q1p*_t>Ef&1v2svaB?xzT%R1tm3v$edQo82!mvB(;p~f37{Rd-oJf+k)NNi=y|h#2p*EtG_V8 zS2Qo&ZJj9#1Ig#tQrMoTy}YslrfBO@UJ^m$(^g$ja1OmCS$mXkwlsk(uR;$*ud;U8 zWApKH!;UZ4rNj$mfpI_`TL?5ZlcQ6EmV>GWI!JA-YD^-}*Ov=lcT2@tR#CTGY>dj? zVdS>7R-uZZ6eDvzt56IJDh{a4&s;>66Dn!T?2dY0IRUB=_hOvST3O!OxVX9jIy@U% z2OSc=Si^8hvAy5Hj+9K*00U}KMKd|k$(ZkdI@rdrsHmt|%o?iW!if`c&+%@M7_-$> z(cD6*@dEqm`YPUPpgW7giB#TrK=y3vkuZ$Mbi$Btq|0Vf?i%#ia&7v-gj*IS7B}eN z86c#9t0Wl9*!+poRq$>E?GL%~x(LWs?he2W7`Vw$(u5utw~y<~rr{~~KQIOIUU?0M zgnS1*u!M~pC(Ly?+P~iU+YY3GA@db<&+-cTK6WAK668t>_)22fPlF5HcZ(L55fQ8r zbDIe+J(Bwl*b!OfA~mLNiF88jgXA(15^X|hh)T5R()48+@`_0&e9es+OpBC}P7N5P7rC?n&!sGIH%J z)~32oJB;reVrxaqVo@WlEiG&zyw6?KqO%v>l`ZAv`mUIX(mF`iSGJ(jHY9h8O-oC} zgjRwn9s6>Yd$|ybIKLP#8xmgDH}SNzL`<|vy|=U7+gZ61EM7M>&s1&k*ox%3c^i9< zdp{c*3;|a*8xmeOil@4+Npq2e`(&V4s_pGFNHVEf-BefL8dMswAmy;T0Ar;`()Me0 zl}FO1YxRPNWUN-?!-bWL+v*wC0*?e?k<`LFUUZDS7n-Chj|7YjOrm#lT}~8V}+U@c|crXt@2n4QsiF{u|kGmt%+C* zCZ<%BLdRMF*@AVU$C{Ipt)hq(G6!pY6)udmsl^m9X*RW4QOztsalogw8MowGZgkqD z)ivUZfQYCoI(0gm3Hp~JcCWg%pnqW_;^kO!@lg@&hL+rh>U!Larh`BQk@AxO9Iph! z@hd{Z*i#3i+aP*UbW*vDOvi=y3sJPVc%iYmu64N!7DN_#OEoQAT`>xEw}i2_kTB35 zTZo1Q3%ALq@iuMJ>{yR{Srbv#Aj)u;;bF83qOR`DUz-stMtWIUD3-$F%K8dhGc>Uf z#MI`Jm8DyPu>p#BTa;&VWaSylBrA*MfmwAcCM{?V&LN>sIpVf@Pdd&C(b~>u)Su{J zm$cQl)-h~^>Tr2&HScOf!RBZSEhigu@pUr#u@%DzjFu*flO?v^)CpAZfAQ-Hk#<SM*P`cPl%vt(c=qKu`s#y^@MojjIlB3p`n_gaitZBzNKmY znOslEcsOd*@p;$(nO2P^@GpElAybVuD5=M!?PoUrn7~`@#dzd>I_ADZs4uY~^R)^= zCt}A|?!H6aPnn(z3*^<9J$K!g)^rVW4I)?|S=>d>Fuj45i>{Skk~MOm3uUj3MwLx1 z;I2W$BO1E|V`re`8bqNReQU2l)HIz4eGMX)E>f>Slq?w?%f5<{N=l@iGj60rUxSF? z*t}D(L1Y7wTRhDbhUOYXP1dGH$~B1aK;Md>QhHVdv6E{MHE*A55cw+0pNY{)lkO;+ zP0C$^=%vaP9Zg$1t?I5pL@=^UgKw-VeRj1;uAAbFhTJ@eQq0jcDVsMS>-u5ER*6Mp zYq4p8RkY%YMj=^IEydYJjY7i26|5R|ZY(Vc9mSQSJ1$1@8D?S$CUF}-tF>oiRY)WI z?Bv|%x=Gs7s2nJMu5{?@CP8DbF0_{N`em5RXS0HNh627Lv^^*o9zJKlzM?k zy%0-kxQ5MMrRud-a}Rf)2BD`lIVM1(VUx`-HX&f~RBlYwUKnh`iw%R(Ax{OmT`*Ri z2GJD-V#V1jQ@z-ouAuZ}v~0rSz9$4G#m%=rLjg6_eYP#Ef{P+8SmmR*%?wDs9Y0|R zB=xMhx<>jb0!mogsO>7J`RFWO%6kK1+#I)=3t)M0d()_6xeb z*Z~{!P*@D#x=Pb>F)Cz#($=8sjTByC@lu#ejm@YEG^4OMrz;NLi%7b{G&>Ti7jkb# zf`-lInDwp2_lG3zEZ60xL+ zoi|d1&ik|I1-Rb`COhg&9J8O(mdgdhxiU629Vk7pmtrahfk=QJ;xU1ciS|0Nrbhcv z{3$*sFKcY(6g=Ky)2AQWzJBX8=S3}_^4hG5dJ`hWPcK-icndo*qGX@|#0%P1d%0v# zLVU^joLuVyq>7G`fdIWEiXoHY8KqGye){QL^TqR{%prX`SWHal#GLMVTCuqFMEOH- z`b8J)No^RnV1_`1#_fAv{!t9^T3Zar9x4J#iZwAWyBe(x!}Uo*f&_M$RN-<2C5a(g zx$Y0}GP^Q8t6D8n-(p-Dx|vihUs~PVQrCzE2|Bc!>H4ZMn8M_MP}!W#gh6>zEqWHA z!o0S#sN90&QO!hZxtZzE;o6DR^6FY`Qs`uGG8KJNH3CM2DL#7Qk3KSRH~f0muNqPI z<=1n6^zr%iydSB!F0_FYv^pRxcroH)Q=p#US%<*GeLC3w+DGs^${2;Xb0Lez&ZMj@ zZP8M`nAxd9%$Sp7o~=N+YXSllY9wY+*D|SB?TMKI=a_kCU(QSkC!cpBXz|jT-o6Zo)F0N7NH$O<1<9uWq?O-Zy0>0q~O9h^ptxz+7L!*=Gbx zUR~`m;H}3NKTCD9WLg(^nUM|(31Xj=m+xhUC1Y|EMIgmxa^vL@Wef3gzShg5u~&r} zWiqNVDY=9eww0!-Jjy1?Y;O`slp`Ud)_^Rn0p)XYR6bNulrfF++=(r<<$7vY%SW;V zno~HCnJym?ql|*8jV4fcEFciQ+#`)-l&SWh4o&(BbEcf<9c9@~gx0CB`-|x{>?*FcF7+ zTuu~_piY3>aH9*zR0q3vOcPCQ;GUftSXdxxE3%MV!x~#_(Lov_Hf_L40u`DjQr=jQ zaO#A8wN0PwG(>1Q)F#=ve)^)KN{toi+c8!cV0|Lh>Wz^iP_v@~5ux}@nR*$d1X+hY z3C_o&jdb}rMnzx~9?aHezsRiwVUk;^fTZPT8z#c3Faeo{nZt&emHmlBYG&P&iBcrr z8<^`_AEnb&`W#mJto)SVk!evPUP75=EePA8dMP!PCYO~aJNJ_Zm6B8rZTko{T+U5C z%!cVia#`$!*f7f9KY(r}T7}hYA>=~(ZMoP{!wDoXAWW&WRV>n!SBibXql!gw(WGOX zkUsbSfbcaG;K(Zjm^p}ip{-K?4&of?fjhV*{lFMYo;7>Sq_C2k(RM}t0>2) zL+9026$Fu=>jh!tpC^qr+Y7_B2^$F0gHXJRJJ0n5c}B>26mnK}gEal90~I+oVC!m2 zWkl2|YDh`7Zu_>Hc;TCtk;j(NCw!OCln6w>N&o4^K%$MJp}KWJ3zh}yYbqO?Vcs-1 zR!Y-4MFb`Vn3rGKR2SL6dI+E^1wHW5%GY@44AF;s{>|Xf#c1f2c2_RbqKy#a(_|Z7 z9;$|C!sisrFaYdguEU5oFD$_dvMye3ed3aP8(9QH{K)Guo?YuR{%YvCs9sP94} z^GhR3iUX5DMX>)Mbs42&v|oufX~xcaszTN~YxESS{2H-bPUxRB!;qxqqv~)wC459) z#7inL12RL;xh`3-q+wDMFvf^Wq&o(X!0~xVVnMa1O5R;-HFM++{zXkD0 zu|NirYT1uPBEo@Gxxf-VX^YGRYRoe-Vh(~)aNaeFLJ|V9iGgXdu&zNS&X7?0&(!4s z*bIzhV75WYnpvenhpSi2tcc{-aekUHuOLfmO?(p%O_*}SM7qZ8s|@5~Sc$i9ce{2+IcomRf)vPugc8jMNJLaMB8~X*>wv6oP;Yk;)G zkN{gESQ|lIc&H~$trIQv)6Bg^KllKuoWK?2%BJOLeVlRw|-J7NZOhp5A?i89ojvtEo>k z*lFY~ww*9uDMB^AW%&~948eD6v;O9sjG$Jm0ju%UENVP{jmid2HqBhI0Brmb_$PCR zf-S&@KO;}zK!*grf4wLE7^e>}Q8<`E^YN4uUWv!65bRN8WbMyCa6Gn*;aSJ4`7W^! z__lr%JxuQ*dn3r4{!{RM@R5y?B-+@ARM)ZGf;Uoigh+T9vSBI9fq^Kja&lG_l-jG%t-D(4Yp9k@j>L^FLeuj(k($9X(^!H=n zvz4N-t9N;`m`8iJHxtiMD&FnIsdaJW5Wd8{A(&&N0K;j-~vgy$aQmq)qK|4KYxKcwsJvak43siY!OZlR37zUuxJw&eo%>@%I)g0 z@`~zWT^B&tXdGe8k7z=xHY~?ZzUA7op~>H&{>qiBX~_}bk{b`@%GJ{kmZ#*)M=F(k zdo=#sa5QTC$9NBSk=?eXQ0c2XaV?>~{2bSsrS0}RDTkW4Ze{!~wzdJAx{!EYhE~!a zm#(!8*mRTs++5baV!LYn0x$V1+txWhy`wa}XCEDgejdCippOKAd;R*wO63ukMH2Vs zF}mdakU0dTeLAM~mm3+kNicFh<=yB{;E#KQ59;gnqP|w05#S2Etv!bV$$fE1F|Ul< zl!w!sa6KIU;=<3Z=GHfxTS#^kJ22dqiyMiBY12AnQ0JhezuY;uO|=$F_z=LdEI&7$ zd#xD~@Rn3$CZ1Nb9v8>$+b8~>CvaDv;`PNyAOkpVCldRDFR@Z&zdtT7#dWQ`*7y~j zcXPvC&mKGEg|~^e7pm4OQ%z73AZ=&%aYrjI#T<-pT)tR_tMrD9+3;jCy<;jaY=y_r zj%m2hT3>#do}8%*0Yct4BDU%qG0pXOj9qTIMsBPmR=4pNi z7EFf1!Z;O507M-eLNmb~AdO~zdi(mpXaH6+rbW4{1*H*hZC|k!Hw?-Bgt)1>IHv2& zR;jaFrOs~cc}42TnbNA#akFaiw6wN4Qe-IB{-|xX;uQ~W%ayX_-KX&CH?2PkSBft^ z68^pVdT|8T5l@%93?&hlm_+FC&lBl0kSP4E2>*=Ex#{AC#F_BzV|vH5bk^>+gPGp8 zz4VpA9k`fL(kkApUR$H2Wa^|yBBDyfD%{%yPLzKdE~ce8rQv^FS3m77!Q(Js;z8P^<;J&D zacozEF8vj1nGIyn6;I(w6be)>FQ_ys*D4VGj67`@aZ(TX;yhkgbnWfeClRMnW1ct< zG5k~(X-1o%R3-cpqdFqt57feUT6%lrHBMjuzH22`D4Jt%a@!DTU>)(7+gH4d`tWT0 zC2H{8!rkFX+$XIK3uji zb5(t5lHrI(*@A0OA*CW_2;Ij{nb-7Tb~$O>NdjEWFD_3l0@^MS- z#kiBzrDqG$Bk65D-Ouv|@RW?=3rJYM-uQ;jvJD+9_RYA3_xaAU=R3-tYhUr4TE551 z_;Y0Ij6e6}w!Ngn;_6^D{pZK5@7RFno&js(8+&mj`{rJ?X|eRW6df+LZ_}6O!Yc`{ zo7t#VbgNe0zq}~c+t!`@Ix9EtuBKA1`9RcqNBmVvDE%@i9`+Tl>MMw&E18AAx9!#B z8S?&+(&VTS640h1#DQX@>3=aUiljY(YW8;(fUrxd>WBh0yPj(-Oe*VeV)AvM{ zUh0g$WSe>WigeXht2O(RMzfm>l`k2ICh6q7-IhDrKJvbYzCsp3rLyRv*c(AGk+{>A zW(c#oiNiv~7PKwcd2AUw4JwN2HzrKBAsyni2$3&YS+Y?ZS*}QrwN>vto==i18x>?M zQT2>vM~bc#(LLE6>lfc%n!Gwggquu9K82jlyF8xKjo65Hv}l1L$@gGpOG`<%qY*L( z8$}BoU|U#kTQa#=Yh`J#+fi$el;%XAllu6$_x}qqnk1rS7RLvpL4Ql6k{)RWv`#!Z z&0reomK{sBG?~;IPqzXj0r{7N_B67|SvgC8^IX4<{(G&Ar*YuCCZ4`h=76LoLK*BV z>@VrC<=Wy+^N@X0ZIj7n?UP&&o#xu`#uO0_PTmWD4KXzHt-d@LrfO?fyEaSh z+FF{-IYP3#gFwB&i*?|x47NiF6+x~^o~_+SjwiQo&NOwg2B(sWAUC$J*eG?8_XPEB znj8soWAYE$tl6PQ+L-)FA0G%(zfTc5({P&>?(})&f&P~dGOZ$o~UfJ_xc4OVU zMi55zVN3D_ZSU+*AGRbP?BnA~CIj!Ml)aS95Z&*I8DYx)XAqugL{VXRX2!`jhK(=> z{Icgc_Nnd2uU#8$?c!K9DrJ(T7C=A}D~CZ3!`y&ej1Z+h_f+b_;NU4Od7hCndv?E0 zdC9yRWr#3bondplmx^0k16z`vnj!fLR8drCY~|2~HirBh!_{WwV;13(;aUf9EE4M( zgOY&}EPusdomg8!t}`?j=rY*a-t&TQvm>lla^?I_VXY+>Z+k@^ys&UTyspqS&&EGi zs%GVik8c^Y6sZ~XP}#?~ZpXOhnf@^~=%M(>{RZ%9O=+-p8@_qR{Kqx8Wd0X6gD9!h z@!mlX9k3PN9UGwNf(;$(pM1Ulpoa$FCdIBb{RjW5y9W739NQ7cK{d|~AS)t$Rv0j` zbRODZVny#>Z3ox%il_53nx`jw&_kFSIDkpHGTyto|Gr4t_P$BO%ZxG7RyK`0M)}MD z=4Q~dk~v)A*Oh!5WeTa_dhL!sL#+;R{V#@^oS>1>ObCmB^EXgHY3<$pFdcIcP{;cA z*Zbjn%mxewi{e|F(>cAf1s0W?XhBE8sVRih^FtYmmU1ABkT5vf)=>uiX~H`&B|O;w z?eS#)9^LZU(mKmFa}Gieb&-~SvG%UCj-V$)hRPUiVRix&T>U#r`}bg!3O@am-g$O9 zUqgh0JEtSi74 zW`^2XwuNIjTK8r++MeW8kfyYE4V97Jt(#Gh9n*$@={7>NOXyEF?i_1gA&YJrNVA(! z?=%USxpMOFTb}7f@vY5+dWMprD~%7ap1tL@A;@RX!!o0=zjOg<$roIVJwu{&eAl;8 zj6~7WG0lR>xHY9{`stXz7}Yr!Ipag8a>Xh$7#Pt)i3gP9EHn9GIcT(x<0~jL&N#@J zH@#Cr5t6MR2mn@?;=~xt3r;cGE)7$RG9&P8e2Xp=sg^P;0PS=Zvau$fR?04EI(tga zG_el!!gCzHV{OCG=g8CwrVsa)`jm!b^dK{+PBI$bx+cCQKyuu?0jC{$ZkG4Z{qatT zUZ!lbPQYf{Y+XZ+lGKWZ1#8YUtkeMiAWifj5u!9URvsF)OwRsRwo;{Z%53fS;z2_& zb>j#`gc8@oBFvVi#`UU{I}$H*u|11Y=Q_OBhnWsMaqNgW?S`&DKN6F0T5llbivVpCe}Hd3!irS`AgvjEhKEH%CMFh1AoXUT$9 zwy0poOQhRs$CGE4eFu?lL!^^Qe~ju<5})&l64iAie_xZ4CZpdas(a{lz4by=)E1lWfvjyG^X!2BMeGG( zp$$j_V*7q}Kssp?i3U60Awc9sO}-6RjdnfEmu%fd8W-WlGrW?FX=?`&L5?t}FiOR| zjrM!vFQ)ApKbUj8y0~`}SD}TE8nerCQDf?Hjd1lep#!MpkdPBw+mcK66{)jEVu}TR z4|7)F7<2~RobTip$I=#UD^0$FcXc^&3MDOhs*Z^<$8-uqO>t^yi{#MjiEpJGq5?Zo zI=n%MqjNCy__pG~n3d408oIM(J?i;2%6-w`q_REJ7B|iit+VM0WFP$uLs2E@PvTpi zE$Ppq=sbH1Uk#F%|5-~8edY8t)2V5VHBV9ieIu{KNSKD+7ICB3O7FqE%od$uP|?ek zq~FH$BTPKO-b+r=?M~Fv7FYs^dvi3YqW*D6Xm0eL#GVArc0TLg=zYeO_SC- zdrJFG9m1sfIYhp8Vh(q)EggTLR3HgK^812m0kx9Qv}BFeoVIsD1%E&)_?`n;Ly?!k zQ3vLRNt)#iR9&Pf>JcmlSI#fq0MoMOJZ@1mf&Zfy6H!osUr^kFvP$|pN+qYIt&Zz)#CWj z<^h{tLo+oKt(RaDPbQ2nkx4u)fsHa#8jYqnD`oAWn z4t0|lFGmtsDPG;q>!rx623}Hg`o3z?)@Y;K(7s}WYQyS*w7S%24Da+*UYD|2Q5>mh zu=%&|J>S0K`6!N^^8R6y$-d@wacqRj>x_5xi(?x3xz6~b{o>De#GeMGIR0qMJ`Yp# z9}|DHBYt1c-sSaWjwdBq$7S)XW301WjD01 zxIqHEK?it)1c-LLQ3AY42Z-l~`^CGW#*C*&ZF}z7GWDHh~*mOZ>C{%SCI(}A2Y zWJgN%^^Z3t)8=_g@@@T$x3ofdz^F}#g%&#cNLfA^(pfeXgRzb>Yg`V!m8M2#38>rM z2tfu4od<{ydfaJ4T8=_Q=+B{+*^&H0_ws0jv7UWoC1DMWXf?R!=l)C0&TPi87G^cN zOc$$B=3lpSL+oB;LW>um_ih@l4N+Pl+mrk0tRV~9TCtjTdve z#|-Y+Fn|Gzu7)0mT>-gD8-buUvm1KGDy+dsicA1X>^(CPHR=@BS}{m~2l%%vQpp2F z78#~@)~tt_^4*@?M`ty8HUdR9bRj2cWXk*wCMWwZ8VCztdc5;tP5X)ud)qMJN9n+*NK2NZ zUP6pLBn^5Z{vpaPExAS$k)Jh({Y1)SABu&~L*!Y6UDFv3?w@%6+?tN?;QrS3dn5z= z55vG#?>w?Lg2M?j@aLUJZY9KiEI^^cdazV<6_y%AR1JlVlx%EHiK>CY8rD- z4>s?jL2`NsmVVGWoPRGtbHK)!y?TzucdYf`o)M31$7~jQg6%yS0&CtT9wJzvo8i~$ zjylPN4!ubY3{zb~-zK5MY?sitMWG`R@0id5YyM(F2mVVBLYEmGncAU5pi?Dy4xPZk zX_&c#JI@}nI{wk>vJY3!{rl>(F+(oPo>;UiO|GSV_4MGxiVuffl9pKU_dQ@3B2Yc> zn|CGNivJCIY|szOK5Tz|*psjCo!Gbsmz${rLHv9B>w7%;dRk)RHX+`(ePn#w>Nuu+ zK1ie`9@z%jfbTESu+G-=_RV&4+pU?9yB33L&4F| zgu#Ftqv~xv&vK|k#M7K0sATL!IKvH!aLGJ(b!q;oGHbI7tB<_z{k|k5X@9z zV9hl28RS5KnWpRG(m6zX@qkU-wh#JY$$n7y{N7i-A1di@z6TmQXENv?=xf7+Bb03qXg&0cZ@W@)iDfHRSYAUEGC!_o zLXb5bzMHMYVk`qdBj*mQVEvWxbiNDO^t_IDBO3tkVTlGJN8$FCCPQs97#(C=A=JBS zI~02e@vrt==JYkR7Dx|i*wjzzxSfB*v~{A8YggDU!jy`cQ#QnGtYK?h&JCE%FZZFe zVyDfv4~Bk)9?&h5Xn?gItdldiGmbVvF~>m)CaN#a`f@4SqU15k8b9hW@~ z*t1`!aI9S4McQ?wb*|yxD}zo6A8O{he;UJOjJ8w$>I|=u)T4PGhmz1q?kA*Y&=E-5 zJL8AJSibCKyx9I2|ElGCJu8n)wHV}SYvdUM(hP&Xd?~yVkBMwsv1tc2Sp;30JVa{{ zl%bSHFxZrU4xu;gD<%k3yy@*LhN@PWt852DOOt=F>dC&^!yW^5@0q?HHn5)iZ5j^u z({TNc{4Av2wD!lOD3`~2CV=0UhE&#bcJ*G*Gd9_%wSAL4y6;;y*@OCem}IM-`#VKA zrp1po%_v%|HmzyErr*MVd?x-r`ZHOJp}7tRjSLPXxqaKH)&ZNo3?|dO*I<)~@rL4x zzX`#)s#Dq+P)vPj655=kO}ov+oWh_5^L45zhNC$hR#D z7VX78v_wRd!4|(2Bj;{}`o9iR=^)>h2~roN?1%i{rcy0JqLayQqy{f2soH^Gx6Nc}a(XJ~BsR=ZNwYoI z6kkuKu2I~@hd#j6(bD7|Dep=Z-E0P zKLGD& z&XDJ(iiOpDt}@Rp<{WLI?WOj@p$z{lOh1zUP55#GF5;Sa{;2c!Qu@gp&fAB|qf$(w zG@CxJ*QRsau^EzoybRJ_dnq{z*oC2=yf8FDq`_Z?JI1{b5cB-0!|^4fF>k|==*TvHWzn%A7_B#)YS|eK!_^@<#lA!bEB#z$$xkm)=}pJ>!Vmv;=uJd zG!zxPCw(!UA!vf=>3WXl~vU>3v25ZEv{eE(Aacdb4zR6(q-o_zu=#}|4IF51)%5J zGk>m9%v}{et@y13=@!oeo{SIO+4v*~;I6&9vJ_!Q24frGo;L;FKLXc8%4`1)$ zH$A)`Y#GZx)x(QDe65GS=i#4v_$?2Q#C(hk)58@WzTCridKgJltMK{p5TzamotSPw z{|NJW9sj_c>CqqdF#Rmg0a!q_;rR6CUC=TAy;J_Nm{8qWI9D2a(Q`_@37k+^>V^A< z;DZG3k3n}r4HbNpU~H{`n=F{W_*QDZ;KKxSZaSe37ksf`?7N1$R&a*ky95sxyk77K z!M_zeQt*3%GX?L7$%KR&C3uA3BLx=;K1%R>!MdT<3qD5ZmkS;%_*TK=1V1Twyx`vn zo*?*L!4n0i?a6qv1Ro(dTX3G>9Kmx1=L)V7oG17a!TiO$Qr8JC5PY}bLcvc7E)x8z z;7Nkt7pz;-o@i_dISD{1c0dVpqC~#nslq=;@Jzw{W9!cX&{^(3L&JPrcpQu4`3PrT z?5u8_h3Uhc(I43b{c!wNgZvXAuI0c96V(qqyv4)xUT1i6v4MVParr!7@p#@ccrohx zaae&*$VmV?FdOD+pzkcq`we}>A^73eV7@!Xq#{aB2k3maa{>1D2#;o2h_!))(sl1* z!J7Uz!QpFSAM}HhDBcWO}6aMZ@4ljYnrtKJ% z4e_v&K&=E$m^V*`y0P+a6RbV{C|JwCKh%)rUm{q`|7#xoF{r16nk6s4CRm3v9jYoJ zr(~f260GU}5PX(?z+apGLx<^fzK^Wc11Ai}D}pQK>8~DrQ3iRI2>lYl0@VPh!i2g& z9u5(#>GK6A^aK9dcuBCh4m_0AP{@gEF$sFY4IM$ObsC>7|9H^JzpMoNQVgd5uMB<& z`fmhOa6-)>S>+4fQMb@<9sj@`FFYFGEm-U1r#$*unG7>WGvTj&w;A+Rczy~hcfIgv zmMp0IggROtW(d~w6@tNRoDJuzpfeonEd5W0J{$Eh8%;N17_JtKiesGR{}$*B=fy^) z{_64n8CgoG#lroeVEtmjXwuJ|! ztL1_V<=y3iCkehzu&&^EJ} z_L#-A!H8*t(WbI=+D;bJb|R+jL>tM{X%`XGE~5QIO#6p6iRGb9VlnLyi)n{gOxwd^ z+8Y+r#;}-n1u^Xk_WPF3exI2AKKoW;_O0yWh}p-n{~}&iqWdpmI`&`0?7!Hj5VKEV zzhHUT7Z9^Apq?kDo~KSGrcVAP$~qG*lWpPmz-uw~VH>9lU5h0?#GO&*w`)YLPz(wO zR3My@>PHACC}KFnC7c5UkMMK_I!WW$_H8>2p%EAkU8s&h>(?$+<5Y>mMc~hbrWh5duDGPXz>^6w zYFwy_TsR@LMU?(y)dGsN-cS zQ@1(1SUu!$gOoQPL!5~AEWUN#PxbPC+Ai}>emds;d3=^g-j}LBAn)yx_wyw0R|#&G zyx%0ag`4x#cLcXe5Bad*HZ!}x^ehF3cA;AC$>%}^wIwc87nxZK@~@CHA?XpSi`_^r zgiR8)mz7>!TBT0c!GOPA)j52bYIgW?#0))yv?bIskqN!J@bTLSdFUvIi}Cph_%m_p z-P!%D99D}Qbly8e4m$5wiX3#_JDsHv;$7=P^;H*Fh?4-LykD=NUd4s#23P?Wf8AK? zl*2dFJV(D#l{^zq5e-ozu6I07 zs+$~sTIM+U7@~Fk!qJ~oe{lGxs>k7wP2{aWuBNXgzAr~M!yRDC)Wps>d&5jdqaK2 z@gz~xwF{xa?{)YslnlmtlK0>4vi+_%{LC-$6v^*>n?RZ`ROPtm}cq(k=3EW?R%)M(}7_l z1+pM8i0a1ZK+hJFesnO#@gEaJHo(~6Y)2m-M0$Hd z5Xm#klV`Rk!`vWhgL%P+E}Z;;=bqAqIB7J;;Yq;>4i^Ut9G(&^clg*K;qY<6O%6{B ze&z6_;J+Q79&|e#26`0Bc;mrajy@xJ*Wr1=M-HDD1TmY=lY)I6o*xW%_>`c);jG|H zhffR29X>s%b@a1X;!)FIqJA6*C4!8y7m{5lWv>OwG4-YuTTqF31fMd*W z3LX}4xVdEo z<4x^C)e^Ki+!kEm@Ur0h4xb-9;_wB*GY+o^UUm2quPv_%4s+$(9>A^^7pltw9uYw@p>ZRK{g z$Sp(Uc8$nHx8<)&`_*mv+5p2gaUtF{5b3w5lmul|2S>c6hfH%k3yyj}SC<-+jkST56n3#42g z3*JswUAinD@$~ACJ-zylu2CV|G3Cs zm&Fqze_a+`p8TKm_&FJ8OY z;Ew1zm_UCG036R0Cso zYBK11#9nfE&sev^d&S;yc<T-Ec%0idR`%)IVT((MC=yvf}DGRNm{w8Ii+r%Y}RV7fZPu z7Q1bbup*s@nVx`Y9u~80DpVPgpGxKsGKG#WJCdaY|+q@Kcd70$prP#~MWG^pqFE2A}FtH=ckDL zby*x6qb;QC|8X(eLb`63@I(k>3r&qhw$QW~Z6V!8rpIUtG5juk{5r|F=va>HfNzy@ zY>vIOudpIL&0ZO`d1bWBE63%r3?DU*#=huqd#v8!%VMn# zUmm;0;nlI5f$vA!u(rF)I(duXXI_XO2Y-gQUS9=T%YMVw>ke8D|4!=;8&KAnSj%IbXYN8^>q5+sTK&LvhXaiV*BwSWJX94p ze7Niq`V+$Bo-Y0(f?i$t*l@amr{Y7$wn_Z9q#;8+h&1#^qg$(n$s7oI(6{nST!>lM zsGl9_b{T}2<%!a8q$+Xzqt$GOkMZUw$Ei~seY`r$;fd1z`N*}5C61n}qJAV#UG3-v zm<`nqV|mdt`yl-Zq#s%DJwWjAl(`_A{%*YH>!{oDJRJO)m|Z<%e_Iw1jB#AourdtQ z6oq9oaUpIZ8tU+Ih}qJodh^oL)ObUu%qQZLgSh(YL)2@x4*MnG67W=km-zFn4=D%g z!=u0SAAM;1f`8X_*xAws50yH<0C+7{#b}2&2tLHqKZk;<9rsb#aRkN%-nvPqs&G6< zVwKGDA0>AJ@hMcJz4fxO-g?o*;qsqS)kwud9{SlZTFZ`58Ws zNnYlvssn6Z=6HFTOIC5AD)sVGhE-EbKNYK^7N72w;~7}lv+0?)%k+?kj_FyA&-0R= z3bh&O*$BKAD`%{4?+QNGt8W#cYR5HMn@6t6M*6>6jdeUVW``H+SRHx{`|e_IUAx{} zr(2@h98Z(1E%GtMD#B%k&NN?%&p-s03GSr_s;OY?GX z&Ei5g7UudXUghqy3)Q90e!#q?SFbKtw)8@*+??#*?eNy_JH54@3sr;T z`LegZc9qL-h@Sq-j^`R#6DIRQyiD?Pk2)WD(eoJJmAtG0eJyskSoT3)PdJ&Gr=c+lULXW8()vT2Fq@ z%lo~%%sct%nD>YAxk2*&teQAfb=@xceMa(59J?ynf$-LU-8c<>rB{_tn=>!uf?1I zZ2&sVlWQROaBsij5uR=zrVcV(9ODmncm#GGYR7$vc@7`x*)T`JiqwwnG{h(ESf*#Z z%00xcEspcjGv08MH^bj;M>+K0F!Oj=$ zX!kzt@O1Ux4paLobs*Br-%8MyI7;wD(Mwr^vr_DH+h26!J-$nqiO=i!Op(0LR0kfa zx@G~dh5a0rg|)ny{@LC*WsWyanX8UqfN&vZNwPEqp5uNw5n{$9bo4UMhCj_4*PQO{ zXFCHs=CljZ8-2~;FCu-Gehw@Vi_5X2%Hm3I>{F$lbM(dD9-Q2wXa<<6OS@${69dQe%);kx zNzXdo#D(fxUK;N3<|M!6jd$*p@q&)`wq3?M)r84-i64=8zo+sK$M5oi z*J9+!cJ$wZzazHmM#0~ey=U(WzFWpG!Exlj#~X`%&xFqOulLgY6lM#w!@9XQNB4{> zarEcV@+{9!z54Jom2mXu-OkMr7FE=aeyN29z7W53e%|r_3Ko!d+_RT-_$8_Hd<>zX z`UjThH)@c>zm;cISMwb1R+l>bNA-P&Usw89HH>SciuNG=SsfI! z@xGx#hm)$*;kVS~4)>`09p0>d@9;Z{zpK{i{EIpeHi*Vs)HH|RQ}Y~tU!CUgU)8w| zf1vn#YyIxu)i)geP(9}GN2=T5?W$jY%m1-D(qR>hcQ^=+cX&W>iNgbf8y(&=_@%>p z1+O~1Pq5kH!NFf0{;m4h;r)ZX2iP;vbFkq1L}!i^ z{C&}x69nHcIk{!Dd5uw;bl`YSNz_hheyZsU62V80U550(8Dx{V(e(4x_8 z{O}+Hm@Y&(8WE2Oj=;mdC6w*KvOnPn!H3G81oANr8TcGiLVR?v25C4RcrA9&QqRzF zFQP`o$9Vf{#s>Quj}R>|n)AsCqP2|NV1eVw4;DLI5S#~mRteK~jO=Nt7CctcNwzM0 ze7TXIj%i+sPjdBE`cexX_3excdkexVb*{X%nsMg|TCyCq;c=|Xj45ZN9l1*2lez<4%9^vNA*fyu*^KJ>_8m_*Exb~Pz-I(Fo*uxowIP@c4 zyd%B&(oyP2$8+QkVs$i#}zS^J&aGot48RNoRpNVw9Cbo|n#iFAa7@pXr?BrL)-Mne3%=ikHsg zymXd$=`8%j={!Z!2`%o@IZe{p0Qy>#Xu8f8(iR%Tbk6kVH)gq=6d`64>}nx@Gsty~ zccMi~;fOWI1+O)hP7JsHlEwCQx7=>0~f6mM!5Vg=|6 z4$t?}c?wn|EzhYg-Vigt3qjUtK9N`J^pcC|smJFgq~}PK1P?sF7r5_yY1)xdUqk#T zc&hLo@#nd(p&Z!PRF3^mU-R$Y*Q`fbbDtIKJc=B;BC^LgUuuSupdaD2P5vTWJMM8} zd!ys;vL|Q=Jl5-XvdyU*kT`0O){Wi(%nILemMd{DIuv0@sP z(J5XTmANuvIMnl_C7jdM6c^6vCKRUO3@@BBy>QOjA)FB5P_Odp1O`xr&*=zrFAvWL zkEMT}bt&br3Uc`l8peOto*)Rge^y_F1tl)B>-C#-U!)bM*&_{j*f)L&g4C zCHP>`4__60h|JU8BKS~Qt-43>VJZXR{8;eeqHmuUe1xaRhJiyn+Uk)VI8t$brW^bT zj1h3v$Lr zbpsuBLYt>6&IjHPVUkY#dDaz_!z#$(l#2i8iuI5K-M_1@7>csy7@u{X`TQhfe6}Ob z_l}l#4^`LVUA^z|5ZBf?_P35U+d%WcJ?fFoGs0UJ%k?ykx8Qke4#xwHVh^XX<=qIlUCR z-AI&`zgC{_<*UHUSCN;mNnXAtdwce#c=!*@3>TI0rq3cC-Vt9WGTR4$t@Ym7VPED?3H>CfP#l z#9Hq7PsJ`p?J!sDtICd9S(nAeHSLERk;2W3y^OgFpFIfRy79Ts#Fc6AY2eSq4wz}j*z&TwVJMS_yt?-z zue=`h_8k65H6biI?#a9m-wP!T5AIUe**eKI5I2Lr?;O+GyUbE<>77}WDRVfjNP|6l^J2mJ?9FQh z@A~XbJO7i3c+NF(RpC2hY}77@Z$>zl{&{W}lmpwvpfBilvEIn+^RSoo4&s>Ds1nZgcP_Q10ohg_jH>H{c>)Lj?VD8SrT9x1o!QU4=T=35X zj}ZJv!6OBKC^%E_zB8GgQG$;ae5By1f{zk>rrZ~&Sg)&oS8$>5tQTA) z_!YsE1ivR(_e^ORO4<7$h6z4Sc=7~K6?}qVB;2@l=6eAqrC7etq0v9{OPGhMr3iB+@LJS!hIy4>tuwzV_+YU&?h$;5H@5ZXA2OUi z_eI>nVyt2)QwHdW>DGF9*JauuC+?y9SE`l@ zeZJz|t-5ZXtn96_UC5HHSKYvmns`~aiBHG7nfMh!E$-!82)q`*N~JDJ2)@zNX*YqY z9e+b=+e@gv?bXSaroEn7n7lSNxkLhzj42j z=XtaHyZZAgcK+IMey_$l-0gP1@{HLEM}O0uITm6^epLV8QkOWM&FU)-|3y9E@cZgV z4sTUYIQ%#DYlpX~KRNsnPHVGi>s9FvtAKZ!(c#RuV3foCgQ(8$;hpWdw|BPZK0%Y? z9~`W4ct~)S!v_Xy9UdAyO| z`hT;`5#1>Go1*7+JGw>m{9Qs{tKP?4)Ci7jo9o;T_+9{td zdqW$>DZ3afHxqVOjQsx<=_l|;I%3*i0p&{QxcSOvA6$uN{}w0kUllYaCllU z5?H6dQ1%ROl5`gBa?kcC^Dfh4aTPG`Epc;SHvn%%IHVK9By)Ym*}w^W=&rygK>&9v zJ_!Q24frGo;CQY?LJ-a<^DxgZv-CGT{I7O*9_6qK>3<0Y@PA{BvmSB%tK6^g59FOT zE$hJEb6HpQeAh_9dW_GaO|@@R_r#7R103hLrfCR#kY`IA3QIvd?s4MWEFIU&uGbKF zn5+@#G5!!~zvGYw&T(=SjG9TFsn3eNx8gne)oVWaNy; z*y#WD=f%~{4b}DIE7gRumg?q~39Xg2bqxy}8z+R-EsI+lnniHYKj@owT_-RHfzk3X89@} zmz|YckXx9OpIcb$AkK%a#7(lonyp#0#}rR2&dP>QePhGI3>#36_RSfy%YfP%TIv=y zR9CrIa`h{@2+((U$|-QpM6<)odHUtNPx5j~Li5K=DQU^iEoQd!@phIUx5LE~T?%|) zQX>KGkfZ`H4wFGAU#jS^1K>LdY^FyCA=?n13N~&ALOpMcz9_ zh_^UDFE1w#>{{He0JA2Nu~5PWE*_VaU8qaa1sU<8VmRI`2AZ9RAZzLyD@?^P)d7CF z@KsffZ42tFb)7PG0bYgl5^4GVrr_b9l}!d#%lhi`oe;86xGjaa=D2>6)XlLI$L3@g z*Da}7Xr6PjNr+zbl7-@(pQGco!TM37jOOPSL%?3NJERYVFh8$2Tk=(2zM!qHzO}BQ zd}&2FZ)Mh5gv^gd+Oo%}@@b8Y^}2x=`Hn;IpQ_E)*DaWUPv!X9PZdmELuGwiRrQ1= z6_vGB)lCzs8>;Fm8a`#9Oxt26Zh{RS4=u}Em|%SEuF$KSn;RN;SKtdPTB~9X&#wfXtyPRzmtk5Qg%8SD-6^cSrm-6;-OND+gRG$|LK~oWZp=E^% z01+BMQsv0hvY;~);Lggo>>!X{X!t!3mp$<3WIKMHLy<+Qht4Ik4sw33lzljki6VA*3?*Xj>#MH+!UoIhXu;uA*E)ta>s3Q3htKbg)J92y3Hyi(B=i> zW!0B-jxJAzl0w#{i85=gTjH{sqe~JY*EBV@xEOW4MU`kR=dJa+IppYyi&TFayKe-ULAIqRJnyZzn(qSA+;*X2?uW)7>*kKx81IBm?V-_YtBCo~)uGi;Bas zAj+=jdccZqM3i76f<%mp=KuM;S5@EYN#eSjuTt$ z$G5vG@^$!hQYzSa^V3s&bFarEN3!)YXg00{eg-ju6b) zBuA-12@o~lff}Hwkwgv>V0V+?EYJ1q79AtMJCxjNtpE{0FUIP6vYovsGY_WCT|VjM zXXdHP><&FCKNC}77F#1qWwzG8tXRSvp2C`g_TYySmTXG1`f#UYMFR;SMY#%7Vv(`G ze{r*oZPBb*9irA?BAwpe)U-Q@8r-q;#=pZ{p;Q0Gj8h2VvNc zDPd?g=yHurhK)qBw4)!1rl*mxq{Xv9aABI(GQG7)mWmGWRpY_eq^OEQ1*ih+l_spX z)pS!S$C+KI!Wbk4k}unZEg{iZfu-ygzl3Ofua9>NNQosg3zlceg0+uJh-_(P^A+SMA%yB^G&DL_y&TivJmsrbHm&r6zt5!KeX33h3X_1<}3GSEC9ovd#U%j>%wqA?9-pcuDe7^t5TL*lNu`O+_# z^>DVk$coUh&8nd_r(lckkqwa(49hTTn&?jUXui9MP4WOV5kYF7YDrmIhAduZOg1IN z+MT?hVYjY}((U|`YBf7LMtAh^iB(pHIqbC9u}zD_P=Yp5wUMToT>n5VTV2_=q`wzq zf*uWNwC=`$P*Z_4NanL@trDQL;X&iH(Ul_ncY{Spvt-m3cWhgiBZngjwk|4sdRP>a(`jtHp@M=pc z6agWnuh<9U>~ctsR;KJBcA#ALSmhCAjuxVxyliD4MargpUjgg5!&$L_!ditha{x|E zE!moQ1Ru25ns^3glXhs^1Ibp`!GaRu<#@Ht7bLWCOhX*RvYry^!&HiGSZQgk+ciZe z1c?YLY}Vy5lr(b5agiE5D1j*!lnyxIqQ%%M^q8S4u1(59DDIGQSF5!rRp1AvXXxWrL8+NVq?Ar!RBzB`VOt(fW2cQO!*VsyCMd_M!Jb0Vn#a|~hgFrmC8eVk&AM>0 zl3mz{J{WQPca`tiY85ibb{(GwA+O*w>=SSlfDzE8AspZ^Ts7Of9L9Wh z2-GtU?Nlr=vIc;$C8l5k9j{Di2Ua0h%SjA|DTZL0BU68%X;RZ+pJ)xBB`roIsR)A5 zz0_AsGg2yzAdV4u^Nh^0+n-jPQ-Tt#_1L)}t+pnq$qs9s3>e`?wQr6XnUEICnx9>3 zmM$Sz7A3~-LK*5Qhz}@>Uvj-oxZVzzTD4f)&Kv?iSs%2vQ3FEP1v_XsWLrCI37A&g zRKYn!c1P?4R);pV>9}C6=mH>Ru4t28&0m@;$UW@%6L`>5|fZ1%RMnYux7M(s3L#{$;2WJ3r8Z` zTVs2v1V}7oww&)#CQu1zFENeV97|6M0*OT_ki51(ISQCrO5|dWNLkE*%vOq5YN=e% z(>M*_7)QFUxLa=$L- z{N?=>dd!iF^F8A*18A;`d=xsMKHI6eURxr$&s@+~kqhTBVlrqDU=-$Djq+rWLv=Cd zcl!c4QO9JUz<@+iXX+wj8b$ACoVJ=7SHz72eOfH`c1p~}L8N1gr!;OKiqnj~*mDQ4 zd%?a=n8rhVLHp4So!zb*&~OL>dP+BrMGQEZ{)qwy0;i9fR_K%>Z*X0T+r0E zS`-7g>=G^BnpU%eI5&&E^K4dzInFMCMQo{HA`mPl2)j!xX3O1JUj&Ph?`)|&h!kM0 zIW=oHyIQU@r)G=Ys#qu)G^t|IV!^1eE=J9NjFIiTO|Q1UBB~snUM+x(3Dc|fk673t zWK*vN!R^J0J8XvrjHe7C4NvRf5bP+%Z_8tJ;`W&=5!>T3wUzC#nM*6IGqB8sZ06Ys zFjTNHn}H?g1RJxN1>5a4`Y+})DWM5^JB*eno&G~k3tCrz(X5szECfv&`e=EMyBhkrO%)3w^Q-=e`)o*i&w0F2flY zAa!9X!+F_TtX|~>To1sMv)DfeShbiTu{r@zEc;chW!U!i;-H@^!Q0AoE(jVU>k|m# zlGM@~G=@E6TN76x-DO)7v=My^_M&UUHd?J!uyG?}k*RAXW??5ZVKa%gQWLgF&=@8t zTSf!==lr;TI7N}iRL#>()$Xhsc$GiWB;fpn1D=C7I><#F&s_P-76;qqV+8(mi)pwrGPKXNU50vlS%i5~}3%C@K1(-apksmde9#(QXLv>kT8(hF%9s;D zrz0+81!L%(3dLb%pVbZQ+{n{@FvsCEmuCeys08?9H`+l~9AGh)&6*;uH1OmO?4x8Z zRxb?Kb{-JxuXWS#sYv)J6>BZBz;aQ8Tg*)-T&o>@a^4V0nmL=mR2R?}cU66@pl{n+ zp@0c8u2iQtR*FLHxe8Q-?z4RwG>{ym3L+pCtK@^N26uw2?%AAl@UQn zj#4W$X$nm${nr;&M=GsCqnMgm$)i4OhFwI`?Dlq?kmXYlz&nl>;L{dF8r&a9<4_3A zNJxM(MQ97`(z+}iA>$!paa~mDSSR=o#>AD81;UoNHgwGZ$Z@qfzQwiqJRP2Ua|c#5 zr%s~)Xwz&zq}dPcxp7WS9{}vgvsh|u$CR`z;2c-mpd$wN* zS}c=W$mC?IBtt)5K!uMbxF@Rl7^xJM>ZsPS@5Cf1-;RtHj*L;|dqV3fkois4&!7e( zT@|HbZAlf*f!?lse+BAIr9Ur2>jGhzbYM$szT6Y*z`6xcp)-1@nyGaiY=-Y~k57;{xM$A$zj~Pn6%geDt zf*x^Ob-Z?AkcGXGlmDV^VcV`MI=TD zkUX7cLMJ;8e5>^uN-yD*@Q(r7YV-W7XVqLCeWRR(Ero5;I#F!yfC2+IB#y`_ zIi^>19GAJfE+qaSB*Yd>8lNo{myd2(A9elrK?m$2Qj(>qd__NONtP7&km-LT zbIZLr^5>Yub~}Cuh0;Z)tW?;{gUXSh3L%~KQPL%VgA%4(peM%GQ?7*piX}}{=zCh* z!EYikoe2Uq4w~I9mw9?$NbOP2C6eP5q~wU7&Jp=wZxwn}a0u5ZdJEWo(AL%KxacY~ zH2gb`DBoN|#E?lF?chKuNCXOs;_C(tgW_SaEK%Ln_EJ~xK()KpyTo&~nj1tLtZ0PH z>kA}^GJ{>~a-U22uR=%~7J@^lSG+=b`N`&ci+G&Yb)G%=1nw~n=5QSL*2keNm3hYj zIcq^K_uJ9#^2qs5SDrYA7OG2g#boc zHw7WF1FP{ARfs|F5kU8As)$P)}Wu z)4-QRuELXLS3TrKW%O*x*t1frMQgQ5+8A;_wv{{8^vbAVXSt!aeo%Ez?BoUCyKE0^NJb4o2sF z%%L)sE>qsAzQ(m4=Jg^cF$K0;xr`1GP^xuZNE|(}>qYoD~Dwfl=!(yFKZ15PoJj`UVVOp4J6pQxQ z=iT)wCEJ1!Ee=U-1kmnjceITSm%$*9%?AmxuR}o|Y4lZAl+Q&{ic) zM|v2sTzYLLBv{f15!zBtJav!3qY9d#qzb5kSPgwqY%d>R1}6~{azY=2M-(_8JO+Jz1kEczlF^tUfQ4~NEpoKStJ8vLxH!~$1GL(JP&I5!G=XUmYS2Ma zVy*kfKcNmZO~UG-LlNYG)(69la{$${Rw^ZC2T!P@hd>McKQMfvB=>|m$e~@$dQi|J zGE@o@yq;r5diR97ruR>%2hS>V7XtI`|Ku6zR!q`CRxpm0V=hj0{MXM&&nI!ABP%bTLQKRhCQ0{ zkR5|MpQA}o$fO~cAh4n$pzuMh-LAdcUk>N?iQ*w$oS3q*57Bd46 zZ{Rr%{MIV^%d-&z)o>!)%&7EJA2c)cFD3z`#hapd{*amQh)1nr-z@i6{qSYXc+;W0 zrVgLZ9KmfK(l>KU{;~4c;6o3ZE`i?@@wz(P=4ocMmrFMlVWRnl$%a-lf?HlOX_J`- z3-D#(L3T3*Z}Dyd#H1rmdnMRjF5e+rQoN02CqNH>-gN| z(!C0ZFx}<9$D8(CBK*2^{AF9+)&57knjOUPb+kw0CVdldGaQ!pi6}B}2oSD3B-`d9 zzlj`UYb9SbtTmH=8V(8sVvN+z7`?`rJsc>%V>}mK{9dEt-OD{){NBFZ#EdmBY{i?; z{lzfbx}hDf5yx)?3iKuRnuPHU3d&!GDF-(lWJdM}0}{x=7qfo=Un%(}ei`QLoHxDN zjI6QY*W-8aU^DUo8}7yLghTL|L>s;uzbg>F+lHCXU969<)=eL3MxMei%XhD7WE#fv z1;G5wv!j14BkaZa+W_-(rQ|%@2Ql*(mV;p zHoOF3zOtOJd1m<~geT(@aGb9gz7AoIAASgc;WY@i9$`k>FccW(tB3h1C%(Fq;rAoV z&yG{B82%K({0!$oHhd?-7vK~0^KJMm2=leld_6AHKY;MQ;NPt_{$Yf_e1I9DTr&Pq zgvT6XM$W-6!#}|9dr@A-#&fRZr~D4L;a?#<7X8n0#r}OB;Rb{`G8x{CFh4c#=;7|M|*(h8q$7QP4iVBAK5_!SpQgCn0?E zWHT~o!)GGghj4=pFG84~1$O)|ApFnZzvE{g!kpC0$5W*9K@vs(Qeqw;HuqXX@Ap9}#d!kLx*D>?+hL_v$eF%T=Rk1vO6JdU` z8ZsjJ{~h7No@Qi@E$=@No`m}LL_GNoTFshCW+ZRppF^0R9L(DMTM+&&`hTtsZ$)_d zSPTi9eh^Pu#7u;VB4THpYw`YV)@se8zaxXTu!`f9XIo z@^PE~41{Y4yZSFgcr)gMeGyOkFGaWs^j~7r8~^%MxN@M9;Gqp}`J0?ON6r0+2)rTU zQwR}a{NKj=p@`V2p<(Zo(4cogz&3_jXki~H)3k{I{#p|w4Sde72g?r0amE&JqfpHY zQL0rjKaOCCA*jx<1j5TkTokZHVN>0OpX#8Y%gbAs%LTQ!T}cmvp#{fL`e$xbC@Ufv;3OnA$s{+pQ1C)sx3xGk*NvQ+ZBQ2k@*o_|@>2_;t+x8DV-9 zpXnIBg&*s<`K*0c-~2@Ys~;Pd-+S1I-z8Vy@(mzYeT^UeFtvk*Z(wi_C>kTHZ+H;k z#sh~X#dY@^=-aDrA#V6){#_Fd-@@{5O#Bx$+`wvwce{IN%@NjHscn z@rxyCUM>Y7QW zcDUs3oU~}ef8xW(by+tag19RhHas*s!WFjhPDjHLAr0@@HoY)m+^Vnf*Q`uzoQd=y zHx9VhEZT7Sj`D4M$dSEbTe7E&&gIBHV045f+ibjZSeWz31B+MxeAT8Y4|i@Rs~_%M z&!6GW^Zeh&Lo$I&S z0@j!tJD)k=-p*%McRs%An#Wfh@Yc(bZg}zI!((sxM9Pe8T|eBpd3EQL&t9$Bp4gw7PTC#`l8`N&fa#*SritzPWSX zRc7ok{%#mMY*u$}*!Ze#P^&hL+d8lb<-E9P>%f*PqASL49oVp8jQae_K6?nJ2~9oR`J^2O<52zT&MhJ-Tec32+wc=RfI43u?tJ-?2hboHFZiw%1Z{9fX z=FXS5V*G5`YBqlMzn66{JATm~5-Bu}!)RbzuGtdRj^W>H$KkK3jo;XW-$d<*jZ^M< z89S6s!;8nQPHb$HK-aG{bg+bMU~jY*Q}@=5sK$qHNW2rGdl|1Bs8C~2Dv(40%s1mYw;geJ$2N5Pwg}G^j>>E zwz_k(B7eW?slD!dYTVG%TLR>kRZpFA-&2XkF@m#`-*D%q;mcgpC;2&3cyiq}=saWI zw8yjS@M8r7JU9xNw*Yem{y~tpEW)3c7i~BK#6j*MX>7#vi#D8!WdVrmu6Z8I1EuG5 z3izZAYk#H#1XVBIw5IcAtiSx-a^t*6fXxaFcRt^>dLB4ZSId{RP?4!;`%$Hc*t3fO zxelbPw>*f&zIzV-Z1}AT{H@>M^56L~X9T4Mlk3X|+&kx0i}?HXIb-ebp7MLwoP_<2 z^w%uvegliUO=nk1^PGt&Z{r=5&(FhlFlOfeuKQOr{xUsZ4QT!sz~BBW?*CV9obz9_ zC8X;Cq}?2}&BcYdjY0eeVE1!r`1>wf*TMk*E5H!>Z7$iKY=eyvJRjM>10>kE&{i}0 znv1hV4fK5@{@4{A4HD*ia~Lkzuxv!(Y8TviGam1+_)BGmb^K4t7c&g%YFcsZit$E6 z@SXBS9HX#wv|6GxnI0f_be?c>gR~!+ff_$v&&c{R#QxI|l%WXW2KVGX;dm6pHt%#f zA!iikiAo{2qXF-Gun@d*(jm$K=;Y=B&S2ZSrj!PHd+B`G!PS&ZHK$uz+uCQ%oi{(z zxnSX`r=5PrnP;7S&Z2YAJO6^k7hZJn>u5sT;T7l4Ey)*(T}!)ru2|OFSL!ccS*g|r z2A8i`dDRXxBSMc`HQ+I5yv|&jS1*Of7G}$TMA@Xrk{|k72uL7cm|+0aXZuT4?l6wp ztudGH&F{9exB_n2OGk)z@GJQ{PKYh>bm$Sy=`MI`d01ugiS-ts_!1_t*gWwJvl8?F?bDY5H%{x(G zTKJ7QPhfg0;0F=-2!Y=uFue+}zYE+T@FxX6THyNyo-FVa0v{vrZv<`>cyBm5+$X6g z3;bHa&l31JfzK0I2Xn8$#|!=~0#6tCR)J3t_&$MW2>c%c>nEvS5SX4N_ypjvb)Td@ zQegcgb&J62_&!VEw50D6SU*X9t-$n{;M0Tx>nEu{BQQNt#ylYKEPI&1mJxJp|=e`W`e#!x?>p)vuTqZwx zy}dH;wD`$~8gl>~YC~q~HhEVA&%DD+;OCVznq(;s1PmFKy*CN0_?HA$S=tAr3@O@N zFR;o0QR^&zNAL%c-qHL_0Eg+D;e>Q)RtIpH=0`igw_t8^dBgN;c7T7<;t$#t>`~arh`z4JgNgm7e(oMFz1aSRd|7EDa4>Zb2UFj0Fm((EQ?GC^bqT`MCAii*JlA@{T66Si! zb&W9BHLh8N-I_(17uPJpT(h{25av3K#J)w2;3b@Uc7m z#B{IC%eD}H8R;8MyP1Wvf#YV8Cv5>w5NWRr4kO0&pBT`8vYG7DoMN!F$dy4$FvVMV zo;k_G^Np^AY-6X@ya+Eam-{rQnbjUX!@SeOXPVD?_$>3Fhfg&B=HW%=DG#4(o&~J@ znHBJ7_73^O^t{NQ=Wrhko-~@p<~W>Hn=Je}U-+ZEx*+IlU6+|RV=fGM^}2vpmv|}5 zU~Ph!%9TNnUg_b2xz58ynRgk?;Dh70czlofn1`2{FMGH|*_JC~`pv_DwCxuLZNF%T zZD)F3Z2P0Qo22c7=0&u9fwcWfY5REsSETJ(fva-XxliC4R!C#67I?r~5lH_auzF?8 z%78ytoBcihjn*2$^w-EaYDQ(uoBWO~1Kmb7FXFEYWa)Zyu`g$p>GALlrsCnZpk`xq zj4T(PFlq$1lkN3@nT8kn@Fv_hqAiW)CUYgu<0&6*6h3I%R|_As?Q4V&+V)}L1Ix)E zue&nlUB0f2d3VtE_u!-TdS%S}OoxZxZ?$rk^8uRTGRmYt27+ z{5pSTIb%L^HV%#<$8deI*l%HZ^Rm&gB1)*Z$FJec2pVczc3Y=ETemB9@DPagiIjMd*t+n?Cs z_`TnzXL|+4gwbezV^-jNueR}dX|J~Ng+P{mV{?+`%|RPq3S{YJ^Eyw@pM!aOt6A>x zQFMcc$3*Y)@Yu*nYsTyq#p~3#=$Agt-qDL5-Y5FKhxd&pdfC`Na($XHuZq$he_+%B zSjXCn!XK5H%{!d)nVuJU1$E5qFT6S|x_d92Ull#%;cKENJ$!92mamTv_x-ynI@ZHCL^C{mW3b|hw|+m!ndCRzt+4{c%ozZ zT{3=kEWbOV&c-W)yb2NiaKO)x1Y>z^FqUr(#`3ygEPp&0%eO_>*g`Yr_Gs9{pN!t` z;ZFs;{Y><2kN<4+a}VDUZSwHvqThP>;}P``UeE!e$sYbvl=ASGBR+$qY3`0r2K<8X z`8~pC<@tMs&nnOF6Zuy;e}5qV9}xLhdH$ftKg%4!9rg>$=f!^EGdO>ce)(Dy?Po@` zFYXEE)vpBe>Q{q#^=q~iw(DPlzIZ6;i*E#d@kr1Y-wgWV(V#E>E$EBKg1-1}bemAj zy8b|-yiV*fgSRn zA!GJT+^%Tg_ey-v!+R&zdw6_;`%_*Svrpn+&)fYH*vnhINpMZ)l`#_%r+fIogqt@q zW@3W#hNgLSg0rdi%WtGFv|nD7zR-TzEPbKl;rXVGRzzZ{<6vvNRz&cp3WK=`PH8&et6kcj8}qZ0=r?I|KZMa^K!e>Zb(-Pl8T{;GjOT@a(6e(Zl!>Nf_x0x2ub3#DRjKonWlUK$xC8j6{IGO15 zaC0KwucZ@9J-#(j_HbK*J7ZldkKbWi-0qoexf=Jy(v}kw3-Ao5wq;h(mf1mDI)b*G z7__A`Xv=~`tf!rti1oD76DN5(&q%~Js529>z2fXdJhv}O#B=+3iQ9bM^AquWvN#d% zl`cyB%BOiNG9pHlWVioM~iuf$G5=(su%X}GPgV?Z9mGO>02Kc_~<(OkE4G*vW4@~#}R%S z=^J5D?Zf*65CjcC*ein>DDI1s%mE&Ma$vhY1)S3>1J1_l<2-n2^dirjaKD72N6&#D zM!jwhTnl&%CcOgE5`HDmf#d_{z-c%AeGYVU{@;?nbzk&vBjz;9`LTf4!uv&iJ1Ov? z!JKm#uzK;lgxfDb{}22ljV9;QycXUtm;N|-x*R+u@QY0k{9-5Y%!6DR%x>TEaLWA9 z!)X)iqAe!2ySAA`(r<;EP zeo)$SV%@yz=En7iXBu8?%QtZkNn6e`EAXBcZOfTKTh3xqxiV%^(3ZvUjyn8>_Jj`G zb#c&-uQRKVMCmzuhx9NFFVb@(?zag&IXG?cUK+q_;US~EeNo^y1oDP7_M-3AwK0QP zd6LH$&2$fUnY4#{%t;$5SUhQ7gZmModBCgz&Fh8E zs?hwLz_oy8S0c+<9?-lp@J(Fp_q_CzvJPJKlg?5Q@b&gIG4XE}Jweml5XjYA>`64H zd8;|!=UpB6=!XNJ&ef*m)BHo=M|+!ZZw9OTKln876kqo5(8eM7g}Hy%akN~<)EF>$ z$gGetwZFhC4fkBkK7!lz*LuLW;O51)egOAtkiHS#yT(HtKi?hj^Ok_0?_oB%=x=pp z(#N`=$A8qG{v#i5^?Q{JPM@9X)7%#D=k|a~L;bZ1a+yOK?|(o+r#l zKo6rb<_Z4Dl`%gKX!vPx#^b4A4*6L?!_&ceo?is|@PlUa(;XzUCLQXIx{R0L^^ff%?NQ1#XhLXtTh{I-T6D z@9U9|d3mwzPZ0p&jnICk;r)jD0A6br1Z`i)6mn&-Qaar}gVUM&d;D4G8JFg4ll1VJ z{^^GdEF55FGt=>>bZxy{2_6Q3=~2m74@ zPA9qa%YyS=edcM8FUeWQ2DF{e$k6Vc68JRX;Yk9YUZ;aOKCDL>&)IUw&zEp7MEXW( zzzujGp88X20YCp5J@$>k+I>wRSJwtQs(V_3{JcKk=bHn)^oBt9ers^nXtmcHGtk06 z1e(v2HmWYFZC72CO&h@-$`8}?Vta4J{d#HdJIsB)y>AcN>#SO&^WDCj40<#kr|%17 z<^zGA_CYz{sI#``W2M7`_yBUi@!V?e`gf$i}hgFVje zvPaN*Keoeq=h`w^FX3yX-aE|csCNzEwb*TPPJ6$=pAvobc7Z=F=gRI9_%pI+c~Icb z2K%o&Z0V%`{($BOFeB;(pLk%4d&tc3_-|lzx-^dj^6*VFGzZ z22VFU>eGJ*T8~~lkGH|Y-xHZcp7tKW;{;hwnaRw(H-{SGVHy`luAI#T0{719i!!MgL zV;!A;Hm~)tiDq~>ica_N9?`WP-ZT23hxd;D&BNoPCq2Ahw8_I0qThP>3G-(U9~AAg zhofO)beM+^j*j>6AyEqOcOk1o*k^Iwc~aod$(;EMfj=*E=5GYPQ|8P+3;YE`U*X=T zFz*))eT5SR{*s}u@F;=rGCVgmP2ew^CjqAgzT5EJ)B=J3*?bT1B7yHQj{&|^;IEiR z0e1=fRr3wNRe`@|9t1oj@V$8B70zf0{B`qHz-t8_F?R!I>ql_!NdVV++-xteM%;XN z?q`v{(Ht4!Il_^<0mG&z=O}cH>zTlZ1b>*EozO9Uctnjx$M_LZ17KdzXRKrso*W$s z-1m@o2xW2&`l-N&$$0~&Ck+j_p98)T=Lv5G4g1X@4O4{%M4_B%L7B$~XJ)2HuVMmR zJZCsbLBP#X?6XKmOFX_cTIS)l=t{sx&LM5b%XyV40#6q@nQR1i$Tz0vMVbe3x6C1Y zQgp~MZcUseG%NkH<@|}Bx9pJfCkv2%tvOM6i0q(~Z%oz;_PwB8oxxe4g~3^%Q-iZW zrv+z$&Irx|of*Zp)3YPe$tz>diQcFn;B%uJJ$zpDQ4e1becHo|qwjn8qR8o2u;~S7 zGA{|v3tbwVi@Pj17nhZDSu84p-KIOk0~=k$vn%R!`nZo%5b$B*Csq6rfv$U`w~sX@OFA=Vz|%QT=u85?7CjpG zOD3RmVPMlZ)t{2cz=Ghsf*E`bg8qvWQO*#`;rR#UVQ1Z*)Y(SXBdznKk%xpA2yKhZ z=R9p^1w1@kXj3}R37&abgq(U|9SrvS=LK|LV5ayq7y5eLzFuiQ$KbV;TbYdX^y1Ef zp4Xx$-i-I&RsfH-iyn@5(>uE;;cJnmfP92s$?FUG!1X0R{qO6`-{Sgm1NxWerYPs= zX3QqB-bK48R1LXk3*Fzte5%{wkCidT5YHyf^!Vw)T9d+)$9mD8=|(x9agObBvje-q ziNSMTCwUtk$8rt#dxYjW=Ix;Qvw+vadNR(JKgU9(XKq0MJil^gFsH?H2%r4ci_b^5 zdU%21xaY-sN%P%O?`dYeulLlT-qV;=F3zPf-_8hp#AgQf+q3MOS4ijCUVp`2z@O=2 zy%*wsOzJ()v>xaB=sc|me>3og#&ul~^wDD9M=XbP`$j3}Vsox9=i;E8*9GNV5|nf4 zHs$O!Pv_DbF_*G{0`6Xv>E=(uuSOb&ei_{K(i0C=91V2gioKAsXBXh!q0v{?i*d+oVCZ4FWz=xX# zl=CKmkB~X&Z2}(|%w0z*6Y-Zid93dpgKyB_`#eZLguTG;pbLFY;0Cd4G1CZcmwr9q zgK+cWTtWCAq^GWaE!y>n)H^kp>q7nf_+Z|c?$fXw&Ii0W53EKx-$yxwZvuSu{1|>E z&ky9o_25b6V4TaeFoLg$`GM_R$3M7+(Dp`*3d+U@0k4Gwa~$0+@PQ($BLW{3>=)Et z#QfA-_9TFddt@iue9sJ@UE;-k@ni)7Hw6Bmql5Lg!LR$=+b;I$8%0<77HAkkU9?4i zU*LmfzqV1}Lw4u`b$SlR#T#sy91EL*aY1-9%5nHt^0*)$I4%y@U&qD$mS?Yob@NYX zJI4a$VNxa@3;ImKe|IcQsv8TjFK<$ye;yY2Lk}0fWGLq<+bZYmh7sIutgQ#^bY{x7 zgDJz4@qINn=QjdA8f_t-@GDui$p^~zABT-`?^#_0hE4;1d+%B0y0E+DpY%NY5bQQW z0~RA(uNPQPy;KBNFV0&8)*avn1?KJo@Awz^aDg8dSf%a90@K@s_l^nNAn-VB3Ww}T znxh4tEckYTj}iD>fg1(x7Wi0!uM_yS0>4jSwRnDBV0~ibTLP=s|K|e3h{0pelh zLj;~7aFf8ggIy?ali)8GI4N*V;FQ3_0yhi%F@e(pe^uZXfxj=Xj*5)}tIaU6kTkSQ zn%4+SiyQoZ0?!usG=bH#krntP!4C?2ioiDsjEx7Ym;8^ zvC4OrHE%w6mZ1>X!Iqjg`1CzEm9JL@R_y0IywrR0Gk6jpem7Cs#LsJAY2tV9RLljw zk80*}4-c4vhnJgP53e-w`+2T5*L(aM%||_ajk(RkIGy3)>&-nLUZo!|0Noipnee2? z51AJ|yc%zAaBW;;_DneVCUcC3Z#MisE-${@?NSfF&2)MA9VUJc&pXX>kAJs$mxteG zKIP%}nlF0zedZp(CCCp?x$$##Dz|4FeopQkf>Fqv!R_Q~J>aj}dMVq4{|R{;aVjp2Z?4=6crBiZ;9T@Wfj=0`X&(Ys zuZ+1Zm~%c6$mAyj`@p9Hd+cYtZk92h3!XvvVqlNGE0Dc=g8Ag@^1Kgg&Y(4K^E5mV zJVW|maAxuwcw3%c8S_Z+?z?XV&xk#Wx52qI-xeM*#4}(IdHi?%ssD`m9!^y0mBCYF z?l~{M8|@%3KTqN$i%atpGu^}M{b`Pj`I(%nXR3^O#=jpUgA-=4{QuHi>+^1sZ&z#j zUz;y_{EOzFJ-pd`-NP@L?|OKP`I(3Rh&PKk+P0c~J!~SLDbVs0(Xk#L8^toeSMVOH zeS-H;?H84O`U%m^9-bJz&BF&rYdt(E`m%=)kN(-iM@IkZ;mOgrJv=S?p@(nddnqlQ zspwB0ZjQ!`G3IBG)gkj?ndg5a@JD2x*Kzbwo#)SBnziOdtOffEe5;(%I!xeoaz;zn z>yMddkVePR$K{L`D;~kUCjngRakE}t97p2-ABS?_w~bDmX+~NAuQe@EtS7j)5wYJp zB3BO2`9Q^y3;u$rK|!RM7aapw>2H@aFmr^?Svx#~bga!wdK_E;oC7^>A5;K*2Ff9x z@Y(oP4eynAc7=Sn9_{}gI^h40JwnAppD#e$snb#hmH>A4uB!yrJwC{V96Bw8 zaQ%6Y=}dr&Hm-RJN*ZTtqK>-M;|~w!^CJWO@F&55y-wp6|&@hA&@nzqw7YIC8&OYS? zo@ekC+^x$5o-gzHfWV!ik6kP9g1R$Zj^_0!o8|K&f4K7)MtXWdx|6abfHt?%kj~44 zzFQL5K=Oglr5HT((iQYew=XFJOGkl~;>wt1!I_XN{Tt2k&aU7&qiXP+(aPYR##dt( zrB?=)l+Ss1iMhwa*U0m39~VBnUe>-l1inmob+^Em3$N}MI4ivRSApLkx+{ws!R_RB zJ>Z9M^CI5}KY{d(=%pDcNs4w20jn3@59af7k4&2tuMBov@%nxPIH6YtyRSij&k7CK z3C)Zd!M!H|TN|Lfsp#Y(Bz zJ0ovqF0K|U)tNPXfwZ)=zkg z?B8l7KT`{tX_n^mscgQ#uiVpHEWij?s?DxVX_}s#dQwGxI!;I>({1VY=GJt3hX>m_ zOujo;k)$b2nwr|t)R9afrnkSew80hBtg+2gcUaIssoJx&R4n)`Y0Z*Gf#HQvbDO_x zHQQuv(abG>P3Ag6Tc@5hr`nqCV6$71I~msP;f^LxK?qoCq`+-RY70tk+lH=WSdND~ z@X46|O3m}6on<6jg}Qd|vN_e(+TOvxV7MmTrrudWo>{23qqU`_xdq9U-M#~pO-$G> zWdrUwA(?8|p7ce=X(1RsZIXqiT2N$HZ-34Tjui*Qr4efi{R2ySiz-uA77*3WDB)HZ zZ#y3G$rKY%EPIP9JQI@W+-f^Mm)kpAWOI7c^yXAYPhW1S4L7HVh%*Lcp?lA3)_PsB zVb$oP^U@t)a8T_w^q~{ZYw1WyyRz9O13kU9o>F!&htJ|xZR^4FX`n4N)nw=O_xI`m zvHU#&#qZRZ?d@4I6L)?__f8elQ_A-a6pAzZa{2B;u{^U_D)i(^J6R}cTSnq$y5bS2 zuB?(^{OqpMiI`Hp=lS<(}GZEWKLWU2Ng}k?Y-ErPo%Lh3NxXJLx24 zHR6CzPBjBrnd~sGfW}k<^ekd^xd9Gy3~*`UIDR!DCKe!DrW*OIbCzQjNimyPEI&Z) zu$U+mJIGH!J1pi0iw)CaLQpKKZ+A&C1ptW|K&bL)&a%L>5lEeEb;*GszTKt|B7F0Z zzB%R7YYT-J^%2@iG#%*Hv`??iY_;VF5!%wWsism~iV3Xf8=KVDq6&O&8y_9_MX|l? z4sa4NAt#b-HKR~{K4e^q%|=8D&9pOPp)Q`SWX9&H{8F{6D#cdX@|N}#JeqB^=m5(m zI&7)mCuRdJy05?BdpO1BS^PjD;n1vt3#xZUuiW7}P_5-SBk^bU1yhnqs+iT5)@m(_Zl6ttq0eKIzsnch*S~OTG$maCa=}>4a7P6#CiES1A0R>k= z5|XoAedMo}(KK~>D&;D)qAxL!`{}6`SLc%6{`|74MzkZMy&Ytd@*F!wivsD*Ev|;X zdVIU9B43A3C#8a&S01*zKvxIwUHIL&iemN8=X!VBdSQ#tRhI6y#gv@^(dEw|bWccc zi%4&KZ7^Fc=Ba!-(FJB8Mmc?yIjd!)l&qKssy-{^9)ncn9+^wqX6~u07n23{0X-Zc zn6XKYQiBp8YQO_EKv5%!93;T*Cc#;r>)9tDLJ;$+c>>tEbpoz?eztV$t5j$r}MQ&ruJ; zupd*x&~DJ>8kr0miDYR~=1=cG~ zSaGZArc#bGyHJHOND3rhwhLQAqOk%?*)4tv(fD2;?-q~}OJ)`<&yodeAD0l>(#q<` zPEJDMMJFd=qyRC(EXyKjBbXW}8Reyz9(qteW*0ZPh{W{tIh;;SL^?vLh)T6s(sXf^ zES5~f+O?Ie(Q1X7B-I*_1O0ZV#*o`KilJ&J2aPLhSFCHUEVo;>zq^P+S8A@>w~I?G z_I71=53Rdo=Q^6L<({40#*Z$sma8t4X{1)Ia)iv1H5=0+HG3Hb5M2yeSY@)=ffAPO zLX$^B;{qOwwnaf*v}$NIPJ~esWKR0b?NTT*TNE-|wX_;1qD~s*PWjx)v=oc%78j_h z)gT$v)F6Me&!55!hKPK{qLAI{uEn0RrXos})?!z0ZmHE+iPh<*m|Ni!Hgma1brkaf zO7+)bAwa3>T3iyNtk#Nl__ESznazqz0u-3V;;TJARZCLT<6%LgI4O9Dq+M~+ zwx&8kY4=Inz*{7B9RLE_n(LZ1D^3cYBWZ7;7L?ms*RW(PnQiC}q^?zRmeVL2i#BmR z{e2-)1)Xy`hY4o%Sz=a^(Sr4bgD7kFY`{_bOvCw5%F4=^>22!@WEDyU~AXVJiz5&Zt?DUdJbW3|jzBlJ2Ly_%3)(@9tQb&T- z0cK%M4mFcxi`7h$N%jY<>W)pyXl~BIp`F}uyLnGNJWKlfYcR$aij`foo@lXs1F&_o zY?SKZYLu*dK7T+TqA}1J@VbnCyo$S$kcM+T90cawE)IQacva}3Yyv~?x zN{F>Pc|pT&T^FU>`6bnAc65yH=;0HqtPFG5X%V0CaILc~5<>~vMAb%`W^(-lwQO}| z-;(}bj0t))q|v$?142y&(jb}7sGB~Nlpm5#BJXR%Tw#*CmJwjrn5UE+*hN6U^XawO5}1d<@aF%Nr6UH!@) z2za$66^ek6(pT(*adtT*M=MkI5IayVd#v(^GDizhPhPe%kRoMMzOR6F+~KTPKw+)I znK=L_rj~3?Jc19}YfU@@vq?L&?SW*g>tI2N@N&G`<_i+qIHn;EVp&fK^W3M?@Uc-LuqjMTt>H z1v%^x7ye~~WOvFse6bv#>1>r?JyUm0`IWXcLrU)nHGdXwBnlfgOFG78TJV{3cv{H(hv^tm7b1r&EmMK3$B{& zT@GVDI|S+(hjuEK7+C|r*b-APfsR)uv;(V&5@};&@`#(uurrG(2^D- zl2imi=w9k8rWq-fMi9pcym>}u+3im&&M83&)_Uw*kXBoh)MSUXP6mu{quMt|j7&(2 zWzEm7HA|O}D~l52ccBdR6vPLV#V@(uCR}faORZY0ZD$UFpR5mB+o%Dd>w+CL9I~w) zwggNoZmQrMBD*7Y0;@xt+H_p7R&)W7GFP<8uI4Yz737}vz(`p#RkUe$GxsiJP`PK< zjps=!C)-4v+hq_fuHiwXPmK3a7C?4#U?V-l) z5{~fn2ox|3=uW7+^H$3E<-&kr#@>gzf(UrV=W^Fjn}R3~ZAM|8(x`K~x-^DMX50D2yGA@?>zas*5?l z+ZV_I$2cVe1qLL_vQ!rt(lT^DWkgZo&g$q!N={5_ zBP)s_Q?z}HwX&VL00WVR)lJdt)5^p4_Ix#m@Ri>o)9dQC1(@kfsz1g z&0<-*Y0+|>Su9)Z)&)YzplJ+~4+}Ox$10(&72~!%Z7y|WHWu> zV5neYHUmq}2{vXk3%1)N^k2+pQbJSWb{H*Dx&VZn7PL*bS;!XFHYao-7J5M$PFgW^u&3M%TZU65 zK&o0^h7+o{SiQ;%xE^FEXR!|ouxj~0Vm0QXSoW)0J+OW0#epqXg133-To5#dQvnI$ z{-UKdXbgMCwkEDXy34jEXe0U->_yjxZM52lVB-apksmde9#(QXLv>kT8$~T%9s;D zrz0+81!L%B2*qJ#pVbZQ+{jZ~FstBHj%Nips08@q6WT#m9ANnqrCX6!8h8Q&b}lkk zb)j}1^y#m4(}1N&_?oa;1=d>DfaRhF_lBEJxK=y*h#7+QKSa*#3{aPT~s;AP2gwo?R-RPh{k*yWCcm6$6)DgWHS zL^(NIkR6!K9GJ<}UtLm#vL|LQknguYZW)Z}v_juZp-;Bf6^};C(i0HMCMzR?k{qR0 zXwnp#RQj(ks*Y6d?nf~-vyw+P*bKXfq}lE5IO)jK?7%yY7T}u{L>k;5NW-~La{v;c zOcB}wyRuXI@SsPgE4VsWPz|Ht_@u?0CHSyj&E^oK2L||-rRu|&8gEk zEVOC1AJXiH_FO#6xVGi8IQge-b)teITGK%hmj5j>Xj4HMeoVMRNDoR01ov#e60}$* zw~)!nR7r+@ynqTHOE5E6^D$B>D%DY~W8aBMP`(`*EgTu6%J+oURUq@5te-&*M7k{=KX(yhi4XAmf)9GtOFFnK{u?$^U$l^Yl+L%qATvu zC>xIlY+UkUh^HUb6*E{D5i_{re2*;1x-Cf$4qDrai4WL27)H!eEsq&Wz01q7VVRyy zTXnp4VUUHrk(BPh)_xZBKX4s-(To{0>1curMIOf}=X+-27K0=YzCxnZeFrO)bAxg^ zXzy@-7YY#Q1*VIWkp`iYw|5LeDEcv%iYG!8doDA`7o$`YuXvPdyw<;JlX+_b{eW}= z%k)NhYVYW&TfNIkk%>AT|_}lAH?DSeSso z1#P`7XeFj`vMh`i1oB|CNs&6kpa)nYGfRmW4lGe@<3MIB#VfT`^h`B6VV5LxkV4%TPN3UTrmpyNZP(%(tWn{v`VDE46|0>;-D_ zq(Q$tMY>zO2H5xa$+SYGa1O2%*uFzQInHdW0UG-q!YQGU0h(L${Hw>!TpfL*tH7$Q zPLu^|A2ss8R9q#!7)HD-Wp!zoGUUa&EQ~Bc7+XkFu2ctIQ>=BgtPMkkUKgW#3>42* zRLENj^P=^GSRw-o3_OWA<)-AAUQusd=I*+XxO9+^!$Ha|6_<~0SRc207ito_%>y0i z3Mt9bRKB7g;%iAkpHh;^+;T5Y?Kvi~-HsnZp>&ZcD-|~LpmHRrLP%$Qoa7{cvksH^Swnp5$ih7 zo?Qa>mYm5=%PxcH+T!9_ zR=dH@ve^RWX3H^^bDe}`AeI~kqgQ1X>I5FIs=7km5ZBQQndV5(4{*@g@k^V$?O<7< zO2{mcrVtn7bCBjPj4SxhP=$^WpbATt#x>^4WPq@!>gv~utMOckK0^87{38#2u`gx( zTk#~tm>a6Todpk7gSn#+z)0(+AS8BRHJ+jhG3cEds6pm5S;sNSL7b3`jF8%OK_<;* z4_4K?r{osGK-F0~5Xb%~j0!KIqk`>}&I-6i-BlQXoj$4xLsh5O`Y0B_fa?)^Fa*5< z8v@5M#v-DD7EMsfl_i;H$mMW2qGATPRRkDV6S0~JKAi9{SJa1 zC7Obr`XD}-j#Rz`7WtxsFK_VIX#^N~;1LlVc(B#LF7M_v4OuFfgkC_*la(x=y!FXS zh{DLH_f868DxPetPv!Eb{3T(G>=`rdgqH1&@ar~qc_RrW6*ds?$bo_L+G=uXwvIb9AYqRQ~uGY;HEpNfo z)zUWPM|(v1lXF?zyP!tJatd)+?v(@^JY6l%$XINcmb(~9iz&fAMy(IO*cOCn@sDaF zfOZd2qit-s40w4OJBYwKiwLZjNDr@pJQI%eG~}Qh`?PcrLDQKu9qD0HbLq92*hWeD zL4>xHgQ4!}YE(h1m{bAP1T(ZRitXidvEU?PLQd$@)rbP;gQu&}M=*;?zhb$C^T-ti zkx19?dpebbw`p9o9WmjT+fti^dA$JC;5chYudAMg}&Q86eQX zL)D0I?FBVhLOnIK_#r*8SQH&rAGKEI#1B= z?^f&3+PjbjK7;8C(XK_w@k7<9QM(nA5I6 z!w^M~2U;KONX{Qs%UY?Fm>oR$j2;3lbVtDsi;~=f&mf0pKI_myi^xzZNbq_*7U|uC z&zjyp_#8X}OU)S8PL6u?meimGG&XqjR*=r4 zpwn^P*3Y6~O5u53r%Y?8tiu^rh${wUHIbPDGpoD}!)a`}0uIw+A(6=Ccy6MGpb9XO zx-Gifv~%y-rLV)Vm8lbm`60z(3$SH%7kb4y?8?Tr6K5<$RR-GwJ0mTN$c{ms&(T6D zl+dnB5Lj4mr0_v)tZQXgpEI<`S_hWewT{nkqf(9zc`*<-@wQaQ3$4SkLaRZDoX{xi z+LC&=S1lJnn3?#vjnsn<$oJ8LrGTRm4{3RQ1|!3hh=;EfZ?1skN-aC1VR(})Om{O4 z{(unvt*l%M?3u5k4~y#Z?WFOM(6DW88cVGlqgFmK zU_-Q8Tae~g3yN)3gw>{2krETPpv6?H(3S8;@KFI%xe|lP${Wcq6l-`6gB`~D0zy85=Hvu=pVR`L%;qci3!j*?)+g#)~kz*`%CM6l;QUy%ujh!t{DCl!o4W(ARE3DVSb{1z72l`VSdhnujOa@2M}%m|8BMM4*GxQl7&$;pw=s%7l_V+UgKZr0#BEuUI=I7d7{rsfJ zSje->KNftrcOS$9&+_+1__Y1a2>HVB0SNQ+Li{uk!-pZv&(yo}PC%HSV{5hXX@qZ` zY(@rccs|0{A>3fY3lZjLb{)U@$r8x4=U)Ng5&YlJrtd=-pH~=plMTNSVSZ-#LL0sb zVZJJUoelGoCU*z&{9c6lDI=Bu8wHvg{?-W&2X*M?t0 zcz7&6k!sVwj4(ef%6XFXj)k1?Gxi-ed@#aSjxi&L+VsaDoE?wyZFnlej~r-5K5o;` zLik36U40pZ-v@fR29n;>5YB+!OKf`MUlR*|6;w++TtXXClk-`rPXiHw%SRk1AwoPH z+qifX5qoYl>^(0U^nMl?08tCw2m@u>ZxO&>Ye%IO(-|+l-Oii4REb9hRnP%YK}LQY zF%o^LPWuAkWg~tVYXieibs{}t8|Fx_-gy(YvphSatwcz{cF#l?3aAH(-g+--ydfPWvshoE zH;S{l@I8r7Reht5SneWydT6q0YPWVp&n4I!@D!>MvT5iB69N-7J=x;*Cd0)FE!LPoKYN1Gf=o z@#>$i+BELr&X>*5KIY-hEfxpT`_b93j*TN(3^ zIY7XTPdxH~WS+F@>F24bnyC+>7`(y$+t*z)4pkjLuw=M%-(jn1tZnz5Mta@;Q4NZhTgU|-*GsB&mht2BF=ZEp1*|rQ9Uk0Jb)D14& zHw=;2#Y0`UK__a=Z<7FYl9JeQyWqeKme$PyqZpg>j#1SG({ z0xB56xGPIS2z$&TVnr`Rlo+wDt*u%St=879)`eP^fL3g4TWsxH`j)m)sa_0KRH{_b ze81nB<(V52U;nTAegCg?^pf9mW}cZdXJ*c<&zyy-wV)C?Nqip z)ZN!HJ7>{rSL(v_g{c}t!muByOi`!0ceZq9cvYpIPYm?l5&S~B1n-vFRMCa`Qr!kL zX-d$YDGF|_ORenQDP8>v_2Rm1*EL<&d|l$YttOq6##ly0L+rE0(o}d?G(_=DQK zF5~9_K&ch=W7AdL(l)fq(j%(woFroWvh);H7fYg5)L)j~S=D7H5fSiURo5GJav|$2 z{5Car>J&>YNj$78dP`SnNjoc0hqbTAu3?LfU~m1(y{fh!>U?7<8dOoFI?{4Do*K|j zK`Tx}kd{B!uiU3H>H#$eorqp@07-AU8N2`ks49Vqs zp?hk1oXH`oc9JV*atS8KI>-j~IW=Q+Y%iMEDbl>aN&-9$!<)qovCrX$;`aGjB@1Tj zVhJ#I)0Wt7-MQ{kD!ZxUOeIBv+;E!JPHsw=o87dXvL%KX*%x*sCYH8ii!W`}FG&ab z1z*~e);=4OzLX)fQz{DZWL<8ZVe?jZ+$=?kZrtaJjy2EmsEhI;WInz9RO8IRp7HH_ zR%yvJ=}hi!{cXpRVf}TfCNRI%y^_?>hNf-gVyU%}mM)YpTuU}9A{>gYD3*@8bTkkYLa-TsJH_^1qt-^&#rB|P`~}rf4{@wc z(eazM0eR{OGJqt4Yhr19m(~4sybIN!CRf+Z&E(BOnYbZxS#^J=tk&bV6+e`OD#ot_ zzbgFV_+5|RZ2Yu4qjFy6^m(<*@l|u?1lU?xg8QJVGqZ<}E*zd$`1RYD;rb)pv7Ii* zV!~lS<@ntUEk?S`}p7iBsxa!Mz1n3GO5KD#3jP-!3>)@Q(%e7yL`X0|dV&c%a~q z1!oC9JeB+m5F*qXq8} z%pD2L`vmKjbU5aaabu8eu;B3$=0w301fL;zvS9vW>(4^aS?<5Yp3{pG#szUju#IZsS~-+>=1Z(<-JLqGBm z?4@)M^K~KALELEi8o`WBsXGPhI{B1Ge-SDpu4;53{M()!wj+$qS02iSxLG z&GVpcto&CB)*d?rYx$=^4cYK@g0=kb^yoi_dWyr8Al=^u3sk2-RmF_~`FVmhy>ma( z&(jb1*YZCQbn^KDvQ{f$bdWa$m&wDY9=$q~@hlPg)q(}8L!k=e>QZ?aAXwAS6&%+O z_}8XOg2fjh40-4YIc@gd%^biuwNCTHj|83JmltF7XE6Q$Wbl*Fe?y^y!E_jmQ znSy5vE){&4V6F)%b(`R&f-@k#xVl2{YIKKaI{_a~LwAUNP4IwW#N6qGd2KYIxSA(8 z2URDozA5+>!M6&of#~CEo8X@b{;S|)a!5}Io+tPn!7mB^OmKcK!yFW0T6YW16a1#& zLcu4Xip13d!Pg32Ciq>!d{es(7T~YNW6;!TZkg@^V48R37?rMLGp@Znvzon~F~_Lt ztBUYmmBe|pn1sAcaIX$cIFFN*m@Xz$_+jrJcz}8ZJlqVtUS*2u)AfS;d*)gPm^aDi zK*KTdv1SxTJj63yd4gwRc9@)FU<5SvI>p2kMcY5q;T)&EqA;3T=IHrqvBM+PGKWXI zwgj{8o+;3AssUjh2mf*K$wlNB1ZO45gc5&6=tIQJ=MKR`lT4zb3W{sSzpFJ=#EhSg zGPJlH_}n1f%{$Hq{uptRPJAZ*>Q;B)%{wRu%5TzaJ@42IOvibL#dAH}XV>h&eXnc*%z0c*$apku2sI$zqO+Eaq6qVvc_-=9q_=V;+u4ES+N#Vvb4J z?-R4%Xa7sg{+E3#vF%%l>Dadtvu|a8N6h|?eU=Tweu|j=6#F1z_CX5MhAhm>SkLLA zW{#$DZ|pp2M-$X~ro}LQJ=4zp@NC0mD&0#kOn)p)TKaLG>EEI5oKjQ`QxCZ?+3H1y z^VMG+E|7CUe2k)hltom*qZBt$=%Q-0%5`{*Dsp(NiaC6uI@#f3b&kUm)P=x@Lk^si z?uE|jFSw`ZlN`ZEs|4s11ou+Sz)ZIZKP#VR;Mpcl6oKH=5uT>0pN?4BT1k=Tn0q&h zu@I`DIGUhFwJ><9C-ajbE$tZp2>i+={%NYk#jpExhKk}KAWBzURLyWYpYNh7t4rnG zQuV2OSNHXNAqtHbzIztd!?okPXmaX0dG|ck1^YJN16~iK;;4G+LBZ#G{Z2ThjjHp# zIOn@OGERv^S?v2Uw62ziiR4w)T z+-0stN3r;2Wf)Z#vFeMXY3fN1*N92)!x4WR@qcE@ntYy)Uq8^Z)N)k^K1T`6GlRVEZoO5f)2rRolcuTu9pe6@NI_*CKTk`B#CKWxI2 zXW}yvKGV!$=Lm+PGOiqdvr@vok9ZlvSo(q7_fZb3A)iRzfBHV#kNi9JeHWvA+4r%| zUk$t-+Jt)SHo@J!w%o(C7Pi@=yteF*?fZJ;6f4{_$8f*U<+{U-<*1 z%RY$X-p2&@Od9w4ea&L?F55Wqa|oZMj#t+sFRuvyLnSW^76sk9C`S1<9**b3erJTI zm$DV6i{hf_=~4X+&Q*HG$vE@X$&Oxt9a9@-l&8B2)ka4jt$y$D7}eGEl^p*Ma(Jxb zS;#jf?mL#0Ro|rW{s74*f>_M?Y_d9O(X?^m7TyHw#*c^?WYydezI*X&g0b z7ghc|WuT|ivOGODNR4;lkM;EMao)PbU~esAh`Q5-&sI-5oTHv~I8VLmaDjT$;gRZH zhexTs4i~C}jP8m;tM+qvyjeM;J}Fki9DSnV*>^hXqFRS1t4kf8qSiV*Rej6hiRxj8 zOVo1?pRQhXc!v75!!y;N9X?0xarj*IiNois!y}fT3sg^sFI4>;o~3y1o-V59sFNI? ztEM?zrp|DUoE&7589um?i3MhgYcg z9KKZTbvUjLGZUUDM&#Wcu2C?X@xUZ~Vtry%+=9~8m&Q!Y){!zjGW$wwen((uIcQf#OlNS39;-?Tk3#Ul$ z>|*De*TP4;s9NXEzi!0JrFMK5OAG2{yleZNzX{fJg!cvOS=Oh5yUUBIV;EkKi;fh0 zl+gPMwz2De>Zjmm2mH7$-CnitGjifcu_Or-5zheiWU@W7w1EB3F#fP06m^OC@? zjq|U>NP35?^E%qEOXk!ca~6MHvD4BBg&tl&C%q9HJg!FZE})a12V~sPCReMWt;wA` z=AN!PeV>sK_2pg37tpbfU{F%SE4`@u4b{7Vj#rXesvp+f-G$!HTtG*D7!(>nxN_w7 zFQAiLF+6ULNxFaz9?+yZdx+iMGY2xXD99pm8OZjYIRw<%YSE(7l`J;9>KJ!t-vTgNK(pcM}~3 zH70N1(cMG`D!NFC9#RT#q66LJ6rsJF=s>qw^>3mB*~_Y5=Ws71%b&3%k#%XJ%)*=K z5Q#3yq?_nK)%7;%COS}c#Rb=2OFzKt?a-|4EqAyF4xPDo%N?YQo9DoWTxh3nxdV-t z!nL{O4peQKMhCUG+(|LDz2y$^8M_nyEq4e?YTH}xK<4FlZEv|l95Aff-Ys`h@5OS3 zj(slf+Frz~I^ogoEq90ttnYuzouTh{%bljXTkgJ|f~lMPwf3Uu{mXFO!PnJanCN+H z+t-x@TNL8-yMTOw08`CEfr_^ zoCROY7^941#;?lCsU76j)Rv=)pcKR5dJfFLYE{h}cE55Fm5nIlSmx`flw~8(pTVyD zC9IXRYL_j-4TrUZX>1bt!5`Kz*cQ`9+ep>*%Upkm0O)e;RgIV~gYZ90wlU)wJz8Jq zNOcSgH?VN>HAsxv!UfVblp1fEpSQRi*Bs{R&Z00IH=^?a*|Xe(G0ex%AIc4>x!}uY z5_fkd1&~g>=t;wC+?%w2bm5WJwlRy6P)r+O)*?U{tOd>})#;R^jm7#e< zRopP^-p4T2dq2;47Z)vWGOoEE3Bn?&#@NFFYc%IZQn^QhOeB=cWx*&Z zAzO0!p@)`BnHh#dpj7FeqzYWnkuZ)04^$5;)R8bZ=LH=ZdeAx@?3Q^!VMNXgIw}l` zyRG3-5eB7QIWK6T3pxt2g$M5&rnJH9C~X(oW@~ilp-n?CE{Ej>7g!sGwt;iPnmLN* z@2Aq0g(4>MuxRhh`(g5pwkG#&S6PdBq~&CztPxOb&M*ngc0oi@F%9I;+O@+>5B68V zQ&HLCIkskKqL{bS29ym$IXSu|m@&X;E*^O!6K%VkO*)6=fmL-YCM{@A&LN>MIpemP zR65QP;oi_OCJs$(+7WrVIZrd({t^&}J8+*hyfyMrAf-%C&D=hoD~#!9k}p3#dn z8UGEV7K`@FkzC{t13Fx>p&cDu3cjokx8AQ+3v{&bY^`&U9$2&&zSq{%Kiu*rLD=T9g>xK_i4|ds7HLSGtXfop_IYYKAxDj>qa^^I7ZT~0g5l9=X))ViuFN7XW7owJu` z>(It1<`Nj?TM7#S5`(E07_io0GqX-#z>UgfmUx8jiQg>o)WPnIjJE4?M%n@vl2l%{ zSCf4qiX{MP#L}M+`1gNh}G@(AXhUrvn>Kynpd5+*w2+OKhVBlC~c;HkLwx+|% zBgxYufV3o*qs_Xs?$D=l!F($f&5Rb5_l9ZWBEMN)a8sIg`Wlua3BdNQ3l@EVom&}8leX*9^;s^Z6a!nPx6o41W=IhP zl7*En#_3J0TynMalB_X=W`sqO%gJg@ZZ=KTp<2@DrH;lfp4QQbqtK1MHD)ah&9tg& z7~Tb2n3U>5xOlFiAWKI4?5kK0gcE7!OdFichqW!?gw4B9W&^XbXXS+BDj8=;MMxs+ zp@z_5W+yjKclGF7!7U8OhCM5KOg#>hu(|oO^31RqVKENV-6**b*0`9B7_g5atVws2 z%Uzh7k-FMa{D@Mxaz%%Y1!xEMN6aT8tJbl}HVvY~uF_Xmo5TtvYm*phgzWNkP0H0O z%xAC+o5oU)IBbwHr&z8Ok`>j(xK~JW?tTECDpn5FD zP8S+fzG<%vXd0F_(;!yQ*aT_E4mFW~yfV?ICXsGFTPpuZYu1(_C1EZj!LTx{S9;4c zqGj7xE86r9GT_oNx_~w_H6yNBYh^$W+p1l5WRVH%pj$J&B&(3-OW2i8iT+<<9pdfI zYzO#D6*5YsUa0LOwxdI^Pe!8@Z5o8`7fho;Q^lml!~nHoRRwj3%)^)~CWk!HRv(Ih zscIfXSB#>X{ajr^={Z%wTZVOHQrvv|D-;k6`awWnU5mig+LSj;>_OaS1!N?bQ6WA~ zfux>Q&#Q2TAWhvCGn>*sB=uS{IYeD&OkS^r=pkOt^&npQeI+^I=@AUTkbxN1vL@1M zCX83JJ`Yd;nw{ZNwnt+SjL z+icQnQ|2r-F|tdf7sefU3mfhs8=TnD3vEYxIX2i?CHc&(bP=+_(ND912t5|1TT1c_ zd6e-R{dBJR;w53`z@Ls5V|2~T*`B8ri{muRAEMJQx@b?Y!zdKvb6pBnG&=t%hFGO7 z202>5lF}0ZnEx%8W=s+!u-_D!i_7rhfP0p1-3Up=q}Ryi$wH=#_6^-Ekj-i=m`mn!IE7Q<6Yt&)hvt6GQ$xauUUmhyp_6x&}?vN z49aI7B1ZO-eDpF7ePkMD!s|7hc}TKtc)gNCA3wZa#UT}A`=wlN(-C3&f(aLy0`&yX zI)pGhb%Dc-Z3Mr+jB%2qeIbiSds1ev(~g*_A|Yn1urSY7pxgt8K!qBL8B|J6sG*n{ zv6&_%$(fvRDX|?vi?t*90tW~oh3RqRJl@82eNKu6|gR-Q-ckN)I3VS&oQ4hPht>sNW~d6t5Sl&CWq6rk$(Y=P z2}p67+<19J*+RT1T0f6w^dZzRlVO!f$|cmFtu%f%Nj9~{&ykSPOc1xU29(dtuzaYZ zm?aLe^0R9yXX%AHEeXjIXinikX1aVp3^NLMJZwoiQ9y#4fm*c8#dVlK>jE;>!P>jjL{l4RiL(N;Fc9@v zsF3C?m(^Bs0ZS9H*M!L`sAgpiRCa2#@EI!anU+5}H-;cQC&ncgkH4^}l2ZkL`=^R1 z+9ayg8xuvOVFporrcAvIQi80*o&;yWJhpWCa#D>PQbiP2`$cXgSRG0QBrQMNFcHrj z6On0{u&&8T*B6ednsrZ>y}*T={@89X6jMe55a_qQ3N8EFf|lS9oVs{S-FL&xp#`3^d{6@l9xM` z%|d6*ltVttp>R&9Wo+K&%%TND$*Zj@h+<^E7lo03fi&7&FAmoxY$W6dv3M1Cy6XuF zjF1Z`L27_H8xs;y0aV0b53!_#LB335b4^{?ki= zM4QCYd9`zEu*kc(qHI|;hBwvA%0%IuAp(;EEErinVV)6!gGPD_`TGGejSr z;MNOBVTy)crY#9WseuO+U#|ra&Q~g z&}+;Tn8CT_YRJ{vG_uY-KCrR$c|M){s5Y95lRQEJSKGZuO0iHEDfxk=Z$0-Gx+mU? zeUH(4l*bpAE?!RKxuyiUvuTnJ`z|yxzcg~BI54#zjqx9i@NxkJwnfX(CgmKJSeeH< zksOe!&l`LZWep3)kDzCeFb~JU$KfS&7A&=POqa}Eq7A2Fj<`gsCvCH$ z5nzpg2bEwg%Rh-^OpYNct`T4tf(TVvLSquB%!VSUsAI4O%V`b{fQwBq{Hx=#3@*a* zkgl!}JkHPa*{sRQGZw#+{Q2zrGs5b5G^pkBOcGieEw;`pPogeZianUci<4+&Rdw#! zre#dW+N8`Q#f83@<1o=&3FLTh{3Jn0dciPQ|w=!H2rlfX>F zO9FHun-qBJ+`3;u)&=m*#UuBHe<@mWt3l*B=L-O1=%2}(mM<^ zjRvibSl}Ub$UtswBndmLFwn}j!k9_{ed;O&U$iE<+G9h({xen{L}W;_GU*8~BTu$Y zqI-blX|58N$)AoGLU+-IAIMHxP)r2h|^+L@2FRsjH|iwNeo+V$6r@ zvqV^0ZmTl=by}ydi|21;_D$ejI;) z*9HF=r!S)j%_;NN3Yr(=wet2lDF#h3;d%8Gi0!qa5k9VK^->{p_bqe?J92TPe!xc(&58&-UYO`+Tql z=HE`ph0hnw2zP^DNPDj}3(p}J@3rC!eMOUUI*RvNc}-%-)qAbH8jPmqtUtuy9OGZ% zo+!KbTGzIDZX5olHva3|Jl~6FniSJ6a951-nejhveC+enZJvML=J_{xro|#k_Hg9< zclb+NQ1}12=h-Y5yw#4z=n=pCmr2WY!ojPIiOf_jrY|<$+OQ$KU=Txs4K2wdQN%}cU%QLx2exe#H-&+q` zu=K>_ix4nsfif?vnYFNPNtGS7m@Z!TFHYA3pyQ>9PmY$Vy+JSNri*Snj*@3H_%pqs zki5|xpTp4vy6Q^v5>3F?E|CWe74gZqwz$g`tLhb|{{c4(mby`v@GpZBytzo;bi+}O z0r1OCHc^VS%^((jwJ5^_&2?n{6S8Q^hcLE%s3l^}e|CkDPgHU1ngE(!#@)62iT1Xt z&W32`ueQ*ccpYs{jBKVRcmar5yb}YQvyYai#QA7c3sV+$S{PXvBt~Ql(U{eQO{%+T zo!#1~qODLknxp%=Z4>r1Y2C&2U21imcQy$FFnyQ;<6AYIci!$lSC@(@#KUOpt#ohW zjI@T5w3b749QrwZVL%_+PE3K3vQCi_d4$nG;?@Etm!Oq&2uk}jOiU5We0wDtu~YZ} z`a<}xX{tM_t=I2ydoW)`0ua%jmScc?V~sltaFYn#1MG`**usp}lO=H(}83D|aF*C8$(VN>d7Wtf5l$`1!7~hV>4)Y~e zg6yZjh$RdYw$|-U{8{JSSUPJtc)z^B#AIrHp{gx6)dVF05)44X@T3?=OvhhZO@xp# zlB_27Y?zp8jUc8aChJ0gkPi=uwc0YGF_XsBd3!e`xz|cktg@w}qIBl_v^AU2T9>Z* zHzf!&oHOuJ{R^3Enz~*2mr%Z9Yp@jN2FdC)SZ+%;>UyAA6yhtm zD+FVL((Z3}6;A z!JFj}Edm(|3*$^E0}yp|h|NUTT^h}dwEE5IXaH6+rbRgubJB?S)UVtl21X^f&cNs% zsk3{;z~~-hVDwyRRWRk7S~M}W?o=sD6f2vNt$0Q0b@@`Zw1Ek)P7_mL5>tvVEfoI4 zpO`iR6L@K2(@vPULX73v_%9OaGm89tKCP9*Lz(yGttx z!!I!5jonpw1Li+rb@5VPVS^l#&EkWZj~XSSSvd!8D4)*kujn+eUGqc)**q+}OSrD2{!q+m)}N zmf1+UUHv?sM4>?C(omsMxmJPbXXI(0h?9E27w5^kqHAxzK2a@OHRg%4mkCc3tEont zpj0LPVxu}j;UA=h@3i#3&}-PgL|7dwu|m-t>509#a!y*^w)&OZP#?C$UZuX-xq?~{ z){<3g-L}LKnOPtll$Gtxq9>@cxu1&3*5G84qg>|iNP9U zFKyb}RVplukD}?nJY;jjRy?iu$os+ClqjM~4kA^j_7FrtCSltY<-7-M zsGXeBBDJ7yZu_Zawi{KL)E`ZpY(qN6n-L>lva)2OHnLoq7O9)pemY+yS2iigSdwZv zfgLHjQY5#jInpV%uOu@ZHEYez8+Uq_~3nUV!xAAfB{{bl)7SVEH@f8jF`y!RJP&1%);>l?S(@1yk zU$P~Mgw}Yv6@UfgUl?s!#wKUwEd7m`PhJHZ_Q=}0SlTLCzml2=Wzb&S*Xp>%eC^KZ z$iAtziA1&bNok_gii4`bZZo(OySA{^X?f2&9niNo6XI1&E0h|o++_Gz8? zl!(w%u2gU?PkXJ#>2_rt0r!(9{f&ma)egka%=h^6oS&?%UG3T>wQEmFqD4=x_7kWV zd6ACXl>yAt?;H)eCV0n!NM4u*&X8Jf{OBP$`k=X8enM zHaA`XyUH?cPU)7pO*drNgElmbFJ#_)!F#47fWF#SzqggGOeP^M(@_uHTqE(A?J!d7 zS7L?!l8TyJ%P+b|-$T z$2A7VCy7`jAtMhBSfjsEoHheI1WoqTx6uop53og9-9xK3=xN` zGi;6zQ*rCj!0tq&4v_dWR8d%GY~|1cZA|$&hpWV*PE%r(?)7CZ5@{J?M!6gc%YZez zvYDX{*P-AmldXL%zxBuLh%2K2p;~lr)p1$~CdpwJC>#wS8?_!^=!D)K-?q{gZsLGjr2zi)0R_;+hiw zK$+5dI^_bjn&J9iOf|J__r0n%iV*?VZ=iru>zg}aIp!#!hRyY_cf#KxTQM0N9ot=< z#^s&e7*V-}7IYL`S>m|!37LwPa3YMDFge=WPzwEN;_HzVAIAUnu|!IXZux9!jitM| z2BD|A;H6WfzA3dKXvvhRGG<$tuVaBLrJ*FH1+!Fy(@$xQ7o_nuB$(bffvhG{8z!aF zn=^8md~)`@w_##B{zatqFJzo&1lW@u8QUZj1=Hc^dJos5kld!dX~iYn*LflF53N15 zg0z|oKB@&ndo1ki+cHtsC$|)He*-UTQ}>8n_IHSh|~YIF9bkazcHA zOF^1a-_%oPdUsuqf^3+Wfk1Z?qFq9NvT^6x;T5vzmVtC|GwYowF*8?A{%y-MZFFo; zb+?wD4A7Lyhe*pI@>&M+*|Jeu-jUJ;q$YmrQf$cx`S_7PMlljaOUE(`7UR~Hq@n<@ zele(V8gj;mM&*iCRxmK5g%XF_RV`D@;)e~vLHjVif->WZgUor;8YLDX+4_M9FbY## z7{l;_3uCrR!xE#c2yBV%)`cR~QdR{pNIwtRSQkqzVV5+CJtbF~SO;3+IShXzb^Xxi z$kGbh$01UmQo%+GGK1Fb>HMCoD==H>RQ4V2&9V;DPH>A;+4n4lNC>^U3Xs1{u)txQt zQOoa8?hDfs%JxjV-8@6I&d#fmee^SoMHQnziS6D}oWi1LykIw94H8%VT}uvq<)l>8 zscDThT{wWgk=HRun26pMX`|OlYr(qAZp|^M7%vp3-A(=xC!VnAnHZjTRO>IUK?D>vvYn#^g9 z2EBw?&A!BqdW77L^3lbiIYY1MI5Vk`r#CE5TI+OSsN&=)Osbzl0t-vrb#-;o2b03iKyFq5>KOWk&tUe zcCBGgVnZ7rm&Co{`@0jD*-)kp+4pxR=C<*1?^noDlee_93mZz|LQeuc2c|Y=2^g~_ z_}Qms`f<$E5QBcmF!1RuvF)hC7-4NMiuJ7Syz?(;re>k_3M}Hug7Foyh^Hm61OEPO zimQTVwfI``3TJTMGRZ~K_+=qEoQSyv#gfEDEk=~v8e`j_PV7Q*yMd!^0z2tU2~$bt z!ptKNu`j-g^-nLAb_4(QZNriY>{5AM7T$NbMT~8s1XhVxv-5g2^s0cD)ErDa8rN-FPonkLH#9jcUDE4&Cp&P0BkB>dw5PPKM5c2>{{77T$k%rhtnNM$Q zt$V4p?uEpU4w7andc6agoTqs(LE2@rFeym8)unq{6Oy?G`F0F2GbB* zk6ii@u5T<|FY~z_#SO9B8%u9*D803Q<*gFotvbS6B|^009TMT~Izl{e>=bJX8#A7s zw(Ys)2&2wg##xZxSh{gtYUpyJ0h?wHU&q%XFz4W&RzzHpK2l7PM$|yLD%OJrJd)-j_H+ zXAN1Be@#zg)J2PUvT_@uT$W_`11htFZ(2G`KrGph& z0F=~QCL?LoDQvr9k^m2c-@Q;JdWbACP4BFO_BQ3aFL9{OYT^P!ifrgYPH>PZ>pNJS zOj&pkMu;$~-IQ2DRjN0xS`+=4*%#8WHbOs@de@ccXYYO%xLeSNBvVuLGG#ABRLOI# zkbMC|CNgzoE#M_(p>zT`EYqn|XL^<-BHGu+fn}zTQ?cTMgF@G#%ffmPiY9GSA|2>6 zi2&851a;DjVipzO3+e4d@y#z9Men32dg|9ct7ya;RzvK4u(L<4F{$rvllp6{Y9@7z zLQ@)Jh@ghW0YNLVx1-;KkIt`T*~B7iiOV^ z@+`uxXpE+(#9y9W(GWd4#g6@+%0&2$hvT1jo?0Km{)CzM=bfkSA{;DGs2Dw1DzXbG z)F7m4DAd-FD)1AL?nZ{VW?Jg+n?KrG(ZBOruqu&Y36FS?1jTCg!I!62)DI%*6XBxb zF5-$!iPUaSnow=xtLP#zn27W)BH}od@1^XY#^=VW^94?AZUst$+N*8mOwAeG80)z< z_E9)(+KAF|^RW6|RE09!qc)^=sKx1uGz_ybp@6E|&171#!x1p{57N2dm9{W5 zF)@kCT&1==+YSo@McF}esokDcC5c1Yyhir6O3Ih)pYmUgZca}5r6j7B=d;OWv1-@Z z6v&z#2+CY;S<`ILi);Qy#FAR`DkEYw=vn^^v0Bkhdkd zt39%D4Ieur~ z;U0S%<53ct!GyRYum z7M!+Z-FvaEhmEY|QOiRgpNDVlAD)HOnp*#i6y=IY%Ls(`r6HB|!j9f+dC{_sTHBWG z!EN8NYMtR6GXDBNT+4Z8ErKSA-o82bQiUDi<9&B9^Gs@qrn-a)mU zcV34;rg^W!=C1LE;)=Zm!M@cH+lI4Dmvp=OLWFB9#VqEQS95U;sTo=Vr(1T?94}sH zedn34U8*hxNoI3HVyo=>tjkc02OB>1iEOOtw|obDNN_Y{oH08R&7@wTBTcJMq;T&e z(Q^2HBEbPiN6vZN<%m%dhJs9yeBi8?QluAeW4_Vl7QOY@{|1Fnl zAw~m<#CMaU9P{P4#0W5vn4BEtTM+)2$oOtXxkuK*T*i6YX1`LvNYxerUuiVMaV`Fq z^8Lc=wwWkNOiB(ph7EI2!W^qJ#n+OhYZOo4V8CnYXi4JWq<5u??mP-=LWjm$hV4(0 zJbP+c8|G;1uciUX!lzwdMck$txQ=(9ZZEY{(DQbRrRlyLE|& z0izg*NN=(j&0x2GFgS6E^iN7;s3dZ9%K(W9J>xqqnHE;O+md1NyBs97A3axZ@gjGE zrlmJtkg+!Q$=cG7*G~J_+6z#8a`Fldx+L+34`mD;U-@yrD^lYt|8+35K1W`{^M%*o zt=NZ1My$=uOGbW_0;%|y&iFI{p;g5y&lQxBs8fk zdn$`5(f_mXtcKV%QYm*%0}4mEV1v0X;LDXK6&%VZ;v z?Twa)r0sqG1l9D>@c+Ahul*1FJmT^EQ{d;n!0-R7Jm&uodGYyr5@8cw-mITb`#%GN(a+-np!@GotP>4 z$IBpf?W*LuR2PSS^5W10kp{oFb^*I0BIfx^$Ky*zV_FOlc8fA`Po_a~iq z)l++J@Y1@)i+TLG1DBnXo0nfOa#Z2y2@@wpV<%0XGWFzBPMtRWw35?j%siv?%(Kou z=iCln6>|9goVjJ?^D5?7E?Br|@sg#>sxGRosja(s`6VkZ{k-?9=UpV3>WWY9FJ4eK zFB2CK!XTWXDK)5kjQkIR2{4HAxEQy=EUQKh1YS^m5kPyD_wy2N$B!dj9BS&)cbZa+ zt{lHwoZ~0m;_1L+@uRx{zc>LLw;|&MaChMsCxF|EUz`9g6%oY+DHZeZw>C804A#Vy`_D3zdKUE6CNG^+LQK*I zLqFG}Z}hMqraLzGY&<@_x&u1aX4?&a!a?oRuQBw|FDdmFaGZw_;64?6l;9%|CcUTN z;{@}3KFV3J{uF+O;NC*76pYh-aF+}2EBI!?nS$>Z++Xl!!2<;UN$^0y?+eZn+yx7> zaWzQr0Kvx!9xeDd!7~Kw#=KbY@j|~!@CkzN5OELrwPs%Tp_qX@D+kb3cf`!K4OJ?Kyabp=LL@zyj}1Z!5;|LEvX9{TU@@d zSBjlb+}zYKQt$)`f2!cgg87fFV{<`g{rM#t=CcyU1#w|M!CeyV)v@z1^lp#-R0s4v zI5ffV*$~$X;J8WZCm!DIVR~;cVe)afyS=phFxPuw-Zpp<>ic-?4#(vSdmWjL^DNNY zi}O)KAJ7XQ6{YjtA%=^Pp9awRZsS~>JCQIt$b76@$Ca*omkZYP4+RgGheNQFAJ=5H zTClDIBw5}%!nYy3<@qfS`{DDB48t^d*bnpP4(J8YO*Vc%{52iWw;TGAhXiT_PW#09 z65N?k2XUk67YWw%ZwuCS@==feEL28Z)#yO@w>>$$iZGV9<54!m{fYy%3OH`woC$Sf zsCJ$N#C|^YjD$ zwfrB`PxJW!vbGr5#`9;vWfK0M9(`dZ!z>Z{6@mq-&QOJMb*Vh`60GSn1jqFQ{*nI^(g<(%&%jsi=>+Xu5F|;0D2{IL6uV-v^!XJoI=c^?`)Zx;+h9iYr~0 zjuTucZ%z>04+)+m_$9$H!8-&`6&ws^TGIsg5q!GfBEd5S zpDnmlaIN681m7fhso*q-FK%uWx)j|Z+D<@^$>myzzX~oC zoP{b9R|^DRDR`OSw*>P|?KW6|zZU1Bcvj>2{wk$T0gjt6`RID%>U?>cDp-45C3vnp z+~(0UP_%J{NQ|@b4+A|5W29P)d&dEkg#kE9sBpF;5)`Vgf@E2NKexE%OV1jW!m#_*2;HzFR=iKpYQuFVJDc%E`#U8<=JWIVqan2zIli#dk3 zm}7X0Ic~R@V|9x;KDU@-a*H_*C+0YuV>V)r**G?{VK^SMm}4-DIqo9nxQpW`OXqmW zVvd)HIbPzp$kI6$BIa0#V;*9Tc{mQSVK@%4m}3u%Irgxa;|+^B#;};<3X3_GAm&(t zeZ8f#uP0_-&;FE{{VDr3V)kq7vxseVN8XV(RuA zP`+7cfvo3u0k6j#2hoU&x|Yc{*vsAV6~)a_w*4YG1<|XYNIV18CdBiC#M58m*(P{^ zrxVZ_8W%;%wrxhy@)-|ZR2{E$PeMFQ6$_PkxH{G0Ty>Vid1|i11z4_C>hDN5j`*n` z-V;1X^aF!6;b+rr240LG9rHrG58<;^p&Aw_d<;UFjPhgxPvfF$w2LPS4H5Exq8i}B zj7JW%iy|jb@#3OtqM6MwUz6~krN_+3i}aJ!g^oT|?%(2LRGp%3bo5izgAPwqFE~6_ z{l?)E^}55StDV4FKBGPPjOkE53{OY-yn|m4$RtaZs$(LmX@JOQrpQOj>I|=}b-&Da zqv}jgR_A!KI@i^*DEcN;Q*lw0=tT~ft5puqlXVn6M%8?Ei=!`4cRRdDJ?ZdLDQ`YT zaiiE4{MC6s)64r=9p;_k>6rJI@ynLHU##9j-cOReUnF^-A-G!dK1*;74~VHHf@{%3 zD)kM)b!HWT{9FvGc2Tv$lg~F4RF$}>y3F;WQMFP*9T@uMZpIeH*d}Z*tGv3jTAl5} z*Q*5%uTj+wUxk#F(rsk9$b?=^`1x&wVdyA_%kf)_ykz0lI9yC)&k67x#qEPV1EubO ze2!J?6mR!@$c0T&~;n>iOBX9 zk?muiY#;AXw#S<|DO=({i)^>4A9hwvJAv1$KZtC#e)}(xt=4aUl=`gs*~UNOqPPXp zwwI{d?&-0=K~vf=&9WZMr>J^ORXF+^t`CZ;zkB-aO?ABslR!<^E(#6)u)}YoWR&`+ zFpUR`?Myh)z-czOKLt4sUTIVMz8edg)y zR<+#GgJ6xror0SjP6=#njjDr#u%Ai|-gaTS1RpqjNboO*(}E0FHx3JInMT#&LB69O z5flOIw)TO@N7tD>9qN3Br=zSe98-shtd0pDI9N3u1B@|Jz%i1R)zLv3@L-|$!mXRy zVT@#Aq1=uO!n!dy(5u3v4-JO6@W%(?7~q890!JSngnD~K5Xv*hlV`3c!~7s@g9X9I zE}oGA?+vAk;-=0#hsOk`I9wFWb$DE`!r>ExxWnUv+Z~=5yyEbf;J+N66f`>=4fJf7 z>BfS$9sQ)>J%^_UpE!J45JW7Wrw4~SJR|7u@EO41IabmhC=JJ&epBwO& zCQUytSmp2q!3_>y7;FF@iE@mq-T}vrlLYq(IIk=b+&AF7a*p7B0Z%Se3(gGi)lchX zg8K(Ny?vG70ReAdxnA(VfL9aTAvi184*WyGgMw|q3a>4%4tl%ttq%q{ye8l^Rdi9cHsGCq8ebEf>TqLlF7R)V z$GDm=<)UR?DdocOP5Ak8V|Y5sZ5Hq!@otv7C3y4@E4Ld&Zf}6TUfn1%(QWxAX}`KH z-yCqvMi<4q8WG>&$@6<&TVC(A<-5GLyuoYB_jqmj-XI*4+!r*O1fuHx;5!cg(39;0 z!EYV?!Qk%>KNRe8_>thB4&M`S972aNKrq1J$AeslpA1GhyfNTC!S9Kjza?_kGXJ*7 zS=Z-VrT*)B{vEIWe^=_iuFtng{b#yO`1$3+_~}?KlYl>#a(O2BXPRo#W$~1!SAXW| z)u%nZ`izN%`TDt67B6~b@sd{-zwpZ9mtI-C?3KlDyt3HpmBsIZdxc}B_4^+LTB*PCGd=&hdFl zg(gNg7Se5GQiNk6#@~dWUniLs9m{b8aJiIYb!7YD5)}MY zdu3GTmCk>j-Wte2cY+{_ zm7S1>V^y(vBdP|gsSY3Stw|13XE^$Bb)Li7(&qUX#d&}wj-Ic=zN0|h;OL{U0;(P6 z@1k3_O5Xb^WW9Bp;QmSLKXz=;jMsd^aiM<(mba#yUR;cs+K%sDE+<_wsbkF`#NkU1#SDJR@ZQ9K{RD4cY?yk+(MPCXIh?Eh=y1LY$D#!)Tz4I%B7qJ>U6cwO1^-w# zlQq@T1@}o>d$;^ISd*(Ymukl%@( zj-8}dIXZWlwBx?&_Z*(A?ss^q3fH(!RzC;*V#&+cB)w{NV>A32hK_mpC4OzZH8l&LHNx{df@?jVZH$a(xySPgZ*SroZqCbn zQl>!{Ma>wkA@FKv_bsZfbYq4nI1B65Rpulb-@RJRbniBJd-RRop3XPaQWxerZy)WO zF27Oq?$^06H_Bf2N62Fw`@)>R>vptU+Egb9IIb>{Hg%Zb6}-p4-7(KCCQjz{yZ9ZA z@LAZs>v@!w=gpoxZ}H^$E%PSxc&jJR?|SmQ)13Zen7iCuC5qE$Q_R~@b+0F%`#kyF zFY*~7{D0facc#LGvd0eAvs&kG;G+ z;q9A0>5b)n>W$@|Qm2`iqUvdNfy2*w`{~a~+oV@iZSuxx&x0fFqBxoH6Z%W}KIG;7 z;STf8@N~@kM*Pl`yl+v3Jyp|u$?uDjcZT8Kt94OmqOkq`);n{u71~<6C@i{7aQJt! zN2ztoOC9Q#QWKv1oQq$L@bf2iBlw|LRQ-v6#6{I#JRV;2&Un1;>5w-(9(H)=dEWHK z!wGLZyvu7Z|M2#m-%(GvbQ9hl^m}TXqkrJ--GAiuWqZZ`9)n>G)7xwQKc2&9x_tlKVVRbhcPT^SZ{giccgjJJf&Y1QI5yr3d^f_B zBRuheJT|5rI5vIBzz*Vy<6DBmoOAyLnt0A7!AGRG=(SOeEUa3Ak1VqZ_6 z_ftoiP@K#6cX$9!6l%vahXoEF>y2BE!^lxPw#_JhzY>0itFgW8zTq&BpW!An!{&Lq zF5f%*SKy6NN2!y|8yqvUJ<~TisM-K1&1f8|8kfbSE)aN zXMXR%@x$AKvqcx}5uB4WCbxY=Gv4F7bXoYlPH-aeWYzZ=)pQ8(dW@OFvaq8f@;}v^ zo1E&+O{U>ww{}sihU7SWs{8sOij|S5qnCQ)_OraX%-P;qwR3P9&c=DJ`nJR8gFj2Z z5Tg%^XYpLOxTq@g<~!x;B}ZT6o$p$rb~yS{IqR5-yz?6wu6ySSo+>gNC-~%~G1$T8 z-DdoK$6(6yL;NNqd=^H)8NF4L-cPCZ|7!q2Zix049R{N9D%`I6ro)Z;F{*L(T3 zt6JpqW*1KsB^tKVTfI8-U2mLro1AadyxrL0SY;BKfg?Xt@LMMQY*3$qA3P%64g4c6 zs=n{>aId%a_wUnC=7PV57l2Egkv+hjZXj^H24c=bNP zKaz80PY8ZM<}5D=e$bnLJ!E1h|C>FYpT`P;cGwg5*5Y1N#g6`xcXs+0UVZqbin}l` zyHlA_jH1GJ^cyWSgp1;9<(FOfS1{_)j_2_b4!=p-MV!WSgI-Y;2?>hXkddA^T zRI|hTRHqah{xfx~!zvi=a1fmAaOVK$LQVV!1-CieCHRfQU4!io9~$g(I6e5M!+%nr zIecVrNM{>oMsSS7J%ZyMJ}Srs{vGOS9P=#pJKF_6B0BR;!9NzA`M%&sMQ45{_$P{c zgp5ULU zmw_t;KdoK@t`Yo@$pHoi*uNT~;HUcyEP52!|0N0Ek^Glb7-zeY* z5k5=x4eAEq+IC>9>B%_?-NyBqz!!ynjGUd&ZM=8D5shx+eS%D2I(!dhYBuqJpf6~z z;@vpnWFPdJ;A7;x0mGArO#I#fJqzavZvhX77L$h|!UMbz&rmPU z=oIfP(5b;PMh=H@OF%y9qUy9D9DAG|tkq=DX9RaTd`9pihf9M$IDA%M$5%X;__3p( z>zx-m&pQ`)fp;!$mYmDtD^big?HL}d(FHuaqW9lV6#1WC%(56S^4H~3EN4{C75W6J zGYbVzlrt)ba=$XN=V4ezry>4tAiUkXxms`re3ZITaCgD{ryRd?v%-1n`M`G+YdZ0P zJa45ORzp5ZXZ>g1x*2ky`*+&Yxx1KptcPMfXWo*(dp*kOK<;t(VgNYqaUZ84@G-Ja zs_A{av2I_tKF0Pq3BSjRnO1*wC(`;Q@Ore)RM6?7ZpKSIz)N?aw=OzJ9jk*Q%(45; zdH-0<_=l=DT>OJ2e!bQ@#EWOBiG^tm^U@mbg&E;azTiBVw+5K&?dj)99^XT{=tnwS zH)G!EtQ5FTb_JH$>k4<4IUq)Q;(r=Z%u?RQ0M$cZ!#;J)y;PPxjJ1 z#oHr3)my)vW^P_#`03tW=V|Ut7t=ixzpaw)3^nvPTShZL)h>$N(6Efo@XDyvl@a5i zZf}uz&Q>uO&)Fsx@^Fq9&$(Va=j|8I!4tJEVJ zVCM=xMs}jA1ou{%i05*_eMHB8Q*d8TclARE?Kmb6$9n@6*Iw>M_&DYQxD&ed5y6?V zuEjT+@U!8Yfse$Gj=F;QDTL=(y$1REg`_*g({=v%{CH1qoZ!MR9_j-+>VcJr=MRX7 zI1bz&gKmotWc@%ntcFagd*EEA!FW6%&ZRS-8~6`g9C^<|t6<%@4R}3jFx%06f{&28 z+9dc$Z@!?{i}*grEe8?6agJ>3Ht(4U=U=_N{x{Rx59;srznQMzk79;as-r}lSu$4n z6?ljvEv`lXL2wV5ueAt1s>3}{JI={Me6vlQtpCkkyCB|!cr5)uZWojT+eNp-bi3GW zWOg8Yukk7J&bGk%&@&pg1^rFI|7lz3nba1-wdTgI#0PTSrW{zeKWS9T-m_W)fer<|y?a*KFMQqg zpZx565$iU>13DwP^9AcuFV%weF3weg^$hSf!JJ(v^<%-@d4io6!Me8nMKE`paPOGl zOu?y`6voZEVt>H{gkC6kpy1O5X9=zpJV@{=!N&@|Rj^+1{IOvDMdhyq>s|l93&tV_ z-APP$n82e14;P#*SkGW53(gk$g@SVg*9y)R+$cCt@ZEy*1wSpgK=2;~>sHYsSg#pI zCX5R89}+hG3kzB`uhOK5Z( zC5#Ob_P<~B9z4b~0;2fm6!Nd#2uyt~UhHA|S{^#OH}8wwmG=nZ%)+RYdc*3#aK3Lx zuI%?SJYCma#?tybrX%D#Ci0VoA8X?C66Y~$B;wo#ydI~!80VXUwa)yf;G<={k#aJ_ z^pbHqf*~zk=QYlB`jF#ryDrM{gXNCvB)XRIUH^Noq2BrY3-xu)A*lR9Kk=)_NN3K`D=jbPT_bC;t^gw%X ztvk!%NpjzjeivV6gMI|$#&0(`Udj~QN5)G-1^4Z6ykx&YW_gW7Jh=#;g;OT5CD0TD zwr9p>2%aqEexcwgQeL_or%HM0ay(h)S-Kogk-DMzJXL1||3)#|>tddGr(9TeXL)B} z&Xzi)-#yp6fA2i+?AZn0c=AH5plgRcR;;XBT;_etQ0}dR%~!Ksm<2eMZ^JKk=e~G1 zo!!Zg;!A+=ZlWp`ey@Fz3h&;jR%f^}s#O;{T&Kz%Ual59yh4Td^L#_CcJ#~Coer;5 z_d1Nz84j;jPdQw#jTayb{N7=^qsP?;4mYSz9li!PG1&ZGs|Gr}PK|WM@6JRZjsgMg8F^H(J}%^>(_Vp}!l1ewv~+z1szs zC>o^G_4#x~Q+D?WeTJgVJYBbEDr?QJ30boBsu}oclP>Ev@xypG3#Z}+pP-ta1zwLY zE2xY9DEKx{r`-;!cKi#+~Gu~}3-;(degn71DLgje^T^7P4b z^1Tm}i>fBvHm6+_H`(3b@C)9V$(Ph^j{Xbp?z>-k--x}8+u&?GzeTJTZ&fck{5yB* zKdN3;Z#()Q-FIHR8|_F}f41W!i;d^6>I8?I-D!@fdPC0DGgMT)m7bqJ?8KS>ZcCxQO`O2q57S}d(|5be}bDuEN`tU&0!TB;cz4v+sOvj}G6fK5#fU z_|)ONpi?KM-auWAt2;!`zc2WEqUUuxx>M_UD8D#r_Xp?;4ikKroY6W)@CG@frTgo< z6)ZEGc65)N(W2y<@H>bAt{Fe3OUHJU3Va;m!M<&fJwi1V0IydCK{!sZw-K@2ivk-D z&-sLF1rvfy#6^d1{epq`tNAaKGcd)%=jaa4APtf?H9r=YgH{TD?Aq7az^5P{(uoh` zc@X8W8gh6Q1@QmI9A`7q`oHD==QEIZj%is3<^tRGu1f{$IX>7$J+z|`;^gxlW;O%* z(eA7T!`QVZj#1|`5;*SVt<(^>uQxvIhgOTdp-F6;8Pa~Q1P@%};+a6c(uALFFU`OV zPnU(?20^+$e~i4W!@I28`+)!3!v}IbryN+%2hIG?9uV6g-M`ZwP_rrDugfOFn}|OP z?V4?hvSS;!F8OYgqum)WwoR_Yd?@esQ9ZL&)4`bP*zcb7{Q$kav1K0{rRWFS-g)k8 zu<_yCHqT-ITc<&Ceu-YZYaOaY9Ot=g_jEWX?2R*rd3U-FSFnKV2YrinpGC#RlV|nCJF$c)ItE zZHX#&^cmh5ex~;w@fo6j8I1GU6)yZ)xG`G0D0+zX4xg{?1Fi)>ah!jEwcysv1)n5m zs2T-NRd>+Q{euU40q#AQ<9j4T@OTH7J!dpL? z@9ozt@V=K>=#|T2ufJR3<$alVo}|v*hR%D@FL&uK_r6hD>D`BXg?Aq^N3535RoHJI^QJ(hA8`5D!bxUg&%wIaM+`W>o zU&%*=zQa@ADCbNv`@LMCUoQ9}FDEfNa>%&invwZM%=SpUo#UtNa8b77zz2pKiEuxb zMtL!h+K;asKOTpRV3KB8b*+<0A>+syDclu8mU+3OMiv(FUkF?W-7nqI-aDg_Zqdks zg1iC**Wz{sn3K(bg%US#(XgD{LS2$B$}lV{h6|g+NOKDiWyRuUb4gFz6 z&x8h4ZmtE-@%Ap4wz;T&QDdyFr>|q3bvMR{h$04Lp?a@t)O=mCVb-Xl>*8%-aFFdJ z`cMhiHMPa0T*>6xLQikLr$4!|Bgxl;wTR&P1)wdq&?Hw44D@OTvHV?(qvEXrZY?NkZey!|6wLd+BT55r7n3ma$9mF7(YiV^>jAd zKX9}XU)PaOA2rH*d-{6vM=|wW{%Em<@}r~oXr-Rtk`bmCl2+(6$Y{h4->}dWk|ONT zuYiWA0lJk~9d3Ywi~%lA9Q&^ZgkS-(X`zv4qm%5b2nuOpv3%g#VIe3KyOFP4J1pdb z#fD)a5EP5-TU=0x03aa&ges4wlm(uJKY=4X z(SdG``|w)KW}ANCp(S0m&}7r=Ai&bTunk&TWP#_rQ~i0L6wAx%04L!Sa-xByrWdkL zg^Y`_*zky+kL9-{4aUN;lBe<#4=ApAA%=90>Oiut z6uWHUqUN~8N=+%cb>X7cW{U&d`EWOQ5#HI8ECrv*@(F(T5prI-j@8vWGUOO>&ePq=2Rd zJWvHTHIm3q0_;(0IGZ|pj>;S(bu=cqxqK&@2x>8!t~a!>7De(vY#wo;ms*DcPoh0BA)WS%$ zYipZKYamD$wba)ijYg%#%5-FN_}a0R ztkG?lQhS#e7F@gNTuQF2^3yDq}XpB4_r}4bXJaWucWxCJX%-wma)R8Z$27 zF=$&9ri*qOnu`LVmjsa;eB>5M6p<|ok?piJ7X{*+G>9GZu^Zx&ETUUnFjdV3!7xn? z;y3#EF-S0Yq_P%;=yvX!?#XBgIb6ir(y0KY^Vf7| zfYPaJdToTVvsRSDrxlNjG)u1yP+%65&-M6FO${~SqcA_MM1$s1nr7~E~_tT zC@ntdGVm5bT?K%EWsN1pYK?+|=Lp){nGe!!E-6?;)H0W$IuJUa?Xa9i(ipUf>lx?^ zk=oEH)7ecR(OX5WBE1FU3p-KLuGxU&>@$wdhjdB@7cBR6>M9BNu#ybduM)*3qkRyo zR}4~dW~O-|ehaELlkFME_T;x{fsrhbtnyr%Gh9$klnzT+YYPes?L{r4> zb;P-~9>X=%qppFJZ*a!+@7)(#Bhp^WcLXOGZ$qRI9ZpzCJZ zNY%s1?ldz(#Wv~;tuY2&e2*-MT*I&o<4hCP$r?>{r?E&bU?w6+ty4`YNzIVOtHfkc zLabfM3mSUs5-(lOug&F>gSIT$ukFaCr6;i-ONfYkx_j0+t|&3e zs33;z;=;dV5ZuJ6k?g<-EH$DP z8?3GRGac*Fb^!5mE{6^BG_>b__);$Pr#AzIw7}U5G|gaP<+IDvONz!^x$M*`iE*)- zi`92!(`ft%gU&9=I1Lo$u@-OTtxjzn&i1OT&VaEf)k@g7)?%<+wCv|t#Y}|~>F3NF zl&tHd1{Ybgd$-7HAX%Smh~`x?&ya>tg4U^q;IYdQYgEQyY(-KfBP%8=IuDbuv3Ro4 zPMZ-H^DrGoNrjL`WwsEbq{(m;<03iN>=n3vrthnc^9iR`&9g{6%z$KMn+CtwRXQAv zQ0btY8tA8TlCr6vYmvrAZAmc|VVvHe%)+)5HjUkER2kNh2W^6KtlHSqnYQY2)$w6e zWo=37C`F?VT+HMM7NQSI+<_yt_iSes(#eh-pEshtg3r)TV50zxz+4)Q1AOI9M<#D^ z9MuJvOZIMpGM^QK=^49rENU?_27s~s48Z`}Um4JLtj>;nhxnkIq6@~^GxY~EP0n;! zCz=CjiHj0RGD1V>UgtBW8Yvcs6UPX=dPbz|)MCYLO0WfMK2{dQRo5gr*>0`EfF7<_ z`{Ia_31Km;dFh(BbP2gKDAE52wxOO**#k=Amt0>aTyMdaS}vWRLWW?UtPiucvJRo+ zg6%ZyvdwKa1&9?VDkwu_b;L?wb|}*_?H9}yRRE;Oie<8@c{N!k9)}eBzL!^$?fHb@H6P7^I&a&w)8K>AOY}?N05)0!)$*3iZL$r0K6pUI( z9_*Fnq52&Wj_~vdCSd5$2~2laZIS-VfdSo&wU6lvJg_sqlBHboAJ^uoLYv9PZmk->!>z0h~m z%ag&us>J8|Zl54K9Q~9GBp8q=!%~T7M5D~#=%=M-#LZFRK%XXyIl2<_w!qV|#ZwxU z56NjnpX{{-%tA3g7pCzrR#1LaL!#SN0}6gI8OwtNSOBEa*Q6U01r7wtdtD+^xePB( zxRctu5t4#Qb!2H#WXf#c;;o7-xe@0du?AYs%+OY?AWLIvjHRAnAqVViKb_2ULrw@5 zlO?AU>&yJ*pLomwoJ?$!Z9$)IWs!iNbX!xA4=kj>l!2SWuLu^Cu$POuT1nXp|Zq5dL1lM<>Dr(m>r=>QONTF@%|%ULa6kZ6)v z#A`uYt!*i@#RpSl$89w8)xMeYOp9c@-B+<4owz)ZMop%qln{Te(S*e$ z+P0doM1n>)LE6$AP(Rm4^+OTGB5{fpug`TSRZ*w>ks<--1rB(o^#f#7P+)Zk1a@)- z0U4AYG}^@z@LnFUF6>d0Zp`tx`A||r2rDWRi?uamvn#DUl}_*qUS`NY8_)T7iw2KsIZ=0`0jF)hn<88_)=>FaX}OA*fiOJs&hjR~eoW zf>vdU%`)OdQ0edsSwSCeGK9@xOP`$^SlP%^TTrXuRE}o_IH&~p;}cpzwmCraCzEbP z+Sb4m7|?T(xjGkW;X$8)d^Z(Xio{+M7OTKo%^I*A)Udtb#0kf0N1vP)qS<zEfUS+bNHP1r(_>lm3-&rcpJ9rEMb`#Q_o`Kp2G~{MJSz# zXFW=1ymo)p2J_kk?gMfYSi~FKQ%hS<_GY)!Lote#mEbZxphSaVozl{#dUccpUHk$q zk(+i=1;m0vD9NcX^@R!uT+q_XfY#42t}P3r1%Wu|ZIYx)7}Nk$WMoMZ-GM2JZXC#D zHhr_EikivAZ%JouS0hhjZ8&W2I0#`CG8D~?oO8Z7BHh#$REY39+A`Eiz^=Bc!(GNg z5#%i?!hRC>?z8zqX8Gl+@}xpPvp}j_b`7xZ?-Q{?q;L*fDbRg~`{X#YtqN$YcL>LX zLOQ5!P4QnnZszjnE6)O>wr-+auKH0U7p9^txryANWC3@9+zNyI5PCCBv2^wvf0E^*104g#_} zNV@&$P2~ma<8tpt4PuvhpaWGQDOs8_>j%($Eh(r|wq!Cl(~DDkj!7)HX#Ii(}Ew+}rdJDPkeD7M%)m+C$ltBsX3z^p!v>-NVC@ePlSjvBG zgrwmLa0vOz#!gCplBwP_Zp6CEvuBsUJ%+*Xjq}=iqsda4avYH366ErL74$BS9N~27 z$==Qty0?R-CtES>dIXYehPu(vWf?33zhwb;8o*1?%NmWdduw1iP#Lzb2^=J-TNExq z*#(g@YY;6cDutU5$(~%2Em{snbZGqPYOqo^*$LTfIi~Ggg|KwQeuqKt)i#Ui1g=-* zT%xXvYwtyyW=~HQ*y(KlrB2>fu#8ash%BC_GfKwCAkAIqSJ*#87TQOE>Rh`n$}yE8 z1B696m%nD5i)1D02cHI z=r=WxgUB=)$06jAM2yD%)OQy6)dMzR=_RJ zU4;Qy=_9Mqm2>l2@5Kb@a5Z8NhM-oULtsBfUxYW%G82?^Wk}{3ayc9huZRJ9U}`h2 zpxC+_K>6L!DKWqktTn81M~h9jUqP^atWY2zLP?C$mZ3y!FBQ;f0>h-8)H$p?I>fG?a@kCl*WM^9Z5ML{1Z8 zW*a4hO{PidEWaKc)NblB&9boE^21HP6iSGHOG_w-?9C{l2tijwOkQ2yE(D}VNf3wE zn9$a89;0k?UN%aJpRBGxDB2nLB8QzZ5aqjZR>cdQ(x$=VzpexJ=kgmeICm;;HdHw} zWVwX45%}P&e+OrDAc?YsE-aFLMCMGFG7QHV(U0^T5=p}z9VL#olg?7=f|!T3Ul?6E z1e99JGeQtjsHDR=SP&)Zj&d+@teG@*RFN&Q3~6vsrljpO5*-sZHiRoOuM%mzK?C(G z>)4&x@316bXq83i*V4T=K)$lEi$BL7X#3ObSPD#;xzrfvBr?_LLg4786V}gI5!fR~ zl5}*tT8cq=j;O)yYAsgj?P^^-QS%nuu9mVPKFTALKRK7hwF`1oEJcXJa;+rT;C8j# zk+Ik?ELSlS79zo3qt?qWwge$s_D8i4K)XxSC>skd9bRr@2Obz_;eqiI;jwEVcft{# ziX5b4Z%YRrRGmrD5gs}<7ha2rWt5~JcxXx48A@(fBMWN9BnwPUAfbIyEHCfHf|KwG zIia_!;RVhIx2sV{P>V^uVz|ZTkxL3qN|Qp{!HUwOS!U*dWT0>GXqMTc11t;du)vJ z+L9u4aH$%2S5$BSIvSr$P}GpY)M#t%=1S07yJ!u(gXt5|szu7trE27;)ryu7U8)Aw zH)4B;p1Lfk1%ivu;Dl48LkrR?f+iWV8`z*0Nb@%}b4 zW$zHfKwQMzR2?rg562441}<_!z3kSOMQCMd(cDaZACS-w!g^ z3KMOokY|%ZIc0SX6Jw*d(9GD(!hNT{N}Uz%d;~A<#F=b zEXfWq2eU-~g;YjZScKv^Lq@_?j(ir@?h9rmlUzkDPlh;E+4s~*p1A`^K`uLgg7 z1SAaiz-RHCBXI6>ewP3dWMeXahG!~ZQqO%Z+=*r3?)g_()N_IG>%#GuWp!Woempw` zCLIUF+4v)UwfLhu%=g(>jCpH-aOok~q>ub2a)!;7d{wvRO#Z1mNDzn_zED+ob1%X;@RGs<#LLkjNsQU;@c9Rd5%&35qN zlg#Mrz<}w9!3S9$nuF({@k=+Xs+xT#o6)|P3m8Cni+iwzfAw5&J(}%r{RA-UF_(8pv?bI;O29j8tk`&(u1|5N6E5$?}eex8W@z7ID|d(s@`>p{UU@cZGw|7E!E4cxzhn`YYQ+wfK3 z^Wte{^e5JR9Nf!m&FDk;WqPcKFQ0BkueAO&^G{Q&G%ZU1I=Ih#qZvha5I3JB`SWAV z=%0ci^j`?~AfD5@$@_#f=N*`SKiqu2mGX-2+u&yZVZW#QF1Rl_!;CINSD>3u z4$z#ZnAU{*V{r2+ZMG}=|97}=LV71z_m|-26Y-Z>_g}%y=OkzrpW&Z^dk*;bp!MGY z_t$5eQMOC^KL_{m=bF*C;Fs?IgI_)a-){XWD`|e1=0oZKW4J$y`eeUi{qKeQX}H-l z>D~u7pHFx6@L7-jr$*_W0QZ-Uhd;tGJwAbO@$1Ye`9$|zxcS`AnKr+9aQ{s}2vpS^Yb?}Yma@Za&X5AH|cKEa0H2=|Xr|L?Qz zZE*A1-y5y_BXHAn`$N|KNw^;i+V=x+^JyVIUBmKx9`4V9-*aquKEpth%xUX>0`3=2 zG^3xg?r*})Cz;VkB>vyR-Fu7~U18Jvd$=D*ew38t_xIqwZ>||lS^uBFP4me#vdjGU z!~Gl7|4QqgfcyPbFt2RG&qO=nQ=gQZ41WULd{(~Ay3c|;J;RKiX2UOn`}$c(-@0RP zfA#fd^fNa6D!6Zj+vRsP-1mdt8u*jm>)>tyz3;H$jW?@`{TS{S7pmP08R|RXz`s`SNUfh!_IW**R}bl= z7ZG$q3qa?T)Z0#;kAd)vk$nrRGr~`9qv1Z7S374qC0_PsZaWxmHd+Ok-Hg*MOYSiA zs!r6aKrLHz`-@vyHWOKgl!c{Y({EKHZ>aD5gwLydou<|)ImJT~wrdP-Ie>bQ=(XJv z#w(sBO^Jbk;EYe&Ym-@SY}&|Xq;4uYs=55~;jx}l9ko@+d3Ir;j_aF7=%7nPWG>4zxo4?~2*YKkm{ezN zZIBvnVM#PuE0A>T?-<#UHdaIH3+k2WLa5vA*3I1VjZJmSE~tRgtkJnD-+yTiK(o=4 z&dz%8tmAO;8voZVuRA9-km?@juj@RYd-Uhb+B$1iC6wnxu7Er4;8)$N@$20GXWERn z_glHr$)g6i)yO-Ol`EZ54BC_?iy9DbOtwk-Id8$ z8xDolq3GZRjkUD&dQZeoc)D2v{|5>@xNriBm313sKTBJRB z3@I=vRJqd0+p(1^oj7>QD>}GUVtBQMSEP6a=Caqg()k#!O5yJ{uXH|uD~b3!q${0& zjw_wdW?uVBXa2K?cBQlZ5U+Hu#T~Xoxzc&jAztbH%?8k*2+!w+}{_%GP z{tLhVs4Jbl_R7_vUg^A}>`Lc-Wmh_Pm0juVFT2uNeM;$-&J5hIcBOOCYh3BP>1+!4&nun(1LH+_r4!c?ORjYO zU(|nirSs{sE1g$@9}n5|d8LzA7OUW9f8dqQ^UAJteggcSW5e@GXLZ??&gaUmbegg& zo$o5U()mf`7hdVSz3fV7U)hz;AD3O}94NcenF4>pE1hpg{e@RL*OXo9ysYd>=fk+d z8D8n^g?ptf@6{LrHiO>qO6P*gmCnkQ&d@+$TDPF9vwq>MS$ea&ZmDi~~kE(8S^v%EnOh=1rdAnsmi(pV+>GcD7=-uVS}PRk0PjePZ$)C-3BtE==#?nNo-H zhw(73PWI!Pkl)$(!?IkoW1qy@41cewgB*ko0DtC>PGbuC)!E;2 zvvPGX0Tru*nEBO_&Se*zZ$tgB#4mM%7`EQr*Q8H-2Vdi z-O%AU9pnMH3xN)D8u)&DZDdyN7`Pjtqq`E}nEr`ye>~7Ro(%WCVKm%yEZ&gnTDz7g)RKtxfUKf~+`x@N8Mtbd*9-5V_LVZ#uu)e+x_w9iW@%wP+(LP=H zBHX_Ubci%Fw=K{i{(|8H9pVJsmj>ybh=zI*b#yj=nvwfiphG+#?(YOT#0%kmFyQB< zaKrSV=hu~RqYZf-;f817#L zI>h_no(CP9GXwVtxO)Sg;b-7}B+wat4(_=~{}{{PFT!05ovbqh_jS0ppgpX#^gRRj zjzCBF9Ngaq{Z2>tJlx+1bc8>I`{6)G_zSo{5$Fhi3HMC2*U#AWkAsYVI?xf$fqNhN zr_&Lh3in_Bm)8;c`Hfh9Qx{Em9fG;He{VZ)?%j?fVBQT@(p+l)4_kEmH`#If54YY1 zi{I(aI%s-u9*wG?i?j06Rv&(FH7@gCn;7`6>mBG{=Oe;m(h9s!+{W0=3)|`&!ss4u ztM`e7K$u{mpGL?7l3Xq;c>_| z{=3Wtos=)Gru#Up$hWPzsj0CE!IeBQ=T4UK<%$)#7D*d$+hRU-*$_j*KFKI7jz0RZ z4NNrFge1FQ?@;-VM;4?4;1`E4-XzdkPQm$ljZK0<(kEg?;6S1HPJih?6;T%hH)xZE>Er!GLcQKMbEGtYrlWgCBITFU4 z`Z`kGo$1U{JfPXr(SMj0qN4NC&Q=sKkfRaI_NV8%{@!a#(lm$sfhk zbNQpi7Rrx~-lLT|&E1eK(fdH`>hfL+0fdI!-ip}*k>R;3dr^P!P~TQNuQeXS>*5kZ15v5Q1xMqRyg+f`HYUEO{shSZ%i@ z4+R0M4Hr}&&fs#;ae%K6pCvtl|4d)ppnHfvkt zrhJ45NrCjqj}Te>q(_Jp?;jyLIu%n`dsIhJjQ%Mc2sv8gg-xE1>Kv^s*rSkk@B~zo zwBFz938<#Btt72t{Cg-*Ks9lEoAd+}shRWylpx&`P)DWVZ0hJaDsv3Ee0QWQmbyu9 zF5ihJf?ABG>kTb3v0%4{*gWDaN-a%sE^{;*Wr_z`l2{tCRVMQT8}I;CenAXU2V(<% zFw*22>f>rTkXT(Q+1IHM0IkTqq=h9H5b;Ut(vtNumPM_t`q~!7aZ7zYF0vd2jhWML9Vnio9)w{%Ci5+nF$TI^A!%483>J6vBhaEa0`_b2 zOb|ou1)9sE=6ZRrj<#6TAAHrPB!vo41satmjJUaYy}WR$R9OlnUa|{srxA@2Skk6J zy%-)ajkT#i9xb>2ZUE_L%8Y{HS)yR<^EUY82|iY`K0t{d4BvRwo6NE-!fXVhL0Gs> zpYGy7B(}+rEAd!!$l$~=NCs6R9ihC{DB0r0Jsn&niX{`ic5D?hHIl7RBhyrngZb@Y zwFtLt)FV|##$54{7}p$KPLSB&!|c}OsdnW0j&O>6I{E0(dPG*P&Ohs*(U(jay$@`y{a$lKzAsVW`-^zkuG4dOR?j~FBv zJW^SULUb#5`24a&C(7C+<|~JG#!9T3{7iRj_5m9?T*TVasQ{&qXr?;@luljKYa^7M zwW1t8t$197YkF;f0<)NWPM;d4CEzB!P)by1fC5h>lrLq|GN~a~bMeSS`I54Q0;eg` z15P#hpv{oS@Ido$Ol_ty;;20|M;>4oJUWA*%S!maED8!9B4}3>bXk2#Luv6rmw~qk z>M8)VF2mdK0RgR1Q1Bccy`6X%+ffS@a2U8DYMIMW9SF@m2p<}QHgP=zQ13@bZRnKg z>?V-ttsGvpx3KTvwV$M2v%!nA&v<{j7f&i`5~wbzKS{ucm1MYnl_)kD?SoX|HZ;eX zndXJ~EvQ-;7#P4SqFa2jK(fl^y>zapkW-5eOMxYmMJ?!9P}*lgTq_=zB~Bll1$h{{ z8tPHkKz8Ifhd_E*?Eca!F{kOMG-ikM}jRTSB}Qi*1uiB=0PvOza6#G}D0-=1Dy4xptrz zeS$k0QfOU`@ySkkuUHBs{Q(=E0ZI!V(5i*bruhPET3*Wqd$h!Dlk;-qut$L$XmdEY z`n`N(7@v^_#;tNt9YL-qc<0+v&?v-*x$JR}>Wp z;t^!nsKaB}(#R#pMQYwa2~@G56wi0r9u-aKA))vLa@4*!HAgFVxvHqt_nS4;tF|!B z#pTY4KeOiy9+fbFPQoEVdo`Dmv3Ssyr3U$6v-Bj^V+j$FPj}Bc#}y?;85P8^U0nE= z41(L*bjw%NhV9$npQq1Lk3J~WLuq?BE7DRingItO>af~F4R>J6k?i1;%^bb7VuQ6+ zf2Lzy+72LI&dJkfDKeSZZl!&$24MG+crCJFa9R^E7%YKekYzIP#^z)c#os;W2 z^GXT3cZ;kBlJ&`kXkI1r3~2}@Xq{>Z9@`(WMnB-9q$MLO=0Q3Sld!RPve8bPF%DxM zro$+y5Ynj3MhfUWIg%#BQH;AVxn^y)WIvJ=s(jVylM@_|SWby2smV4ClEbdjk!wwO z!<@=V%BFsMVt%j#4!0 zz{O0CU?J@DlSgJ{Zf6zJ$&MVKH)^lVCo`Qqa=^JX8V4#KyCTU<-r_i_3oe(G=QCL$ zn4Ym~$D$S^V*r{*Ki|}10PU{~Xgij^6AmAAQ!zg&<8z!d9qnbT6U_m%#4$*sp)x)i zf;`iy8B>iEizAm%JtI;63>W6nZMBwd%~LC^_!!@iGG&8n4_z2<*6?8S+5hTJk_O&$TTKL+-oiqf0)!zot0 z9xq&}qAsW);KEA7Yy!hPkXC1*A$2wftfw8b3tcw>sdEqa)}LKG0k2a@O~KBvfYr?h zi&ec8u$8B}EZ;pNxIS~|WXc~c3o0GF!vxW@nm*iQ2%AIYsV)k0oebmjlHDIi3q-d> z%Lc1}E#V;BaKORq(mcnE2y#-5zf?iAWW$#41YWkJDQkCf9TVwv*o17y`{akLYAlY)q23R{Tdd<4}2h$t!zNUKg(X-Ske zbSCyESDYv>&UBe4_z%WJnUMv;mMAw=O#$S%+#KJc+X zJ+yR`NLm>lJCYk6;f`V?(Hswwu>5ZlU1%&wg94%?5z>QHf`)sIAE}!xlbguohFHH0 z1eK?{WX-_ZLt{y0dLRWVPj#uX0ga2htClb6g55P_qvJlR{i5tqV5`Vfwec(==o%%FP7W@VjBVI5RlK#M}lr3|+<|4SYS)uK;V6A)-ThAQyLrPETI zsGXy7Z4Vs;v!E6&%0dVM+U7u-9N42TtV#fth8F#jpK%DI0%x193n8jwPD7*Z9^9 z|6piORo9LL?8A+>75k zr*BWZ(luemzxCHoNdna)iRwEO6SE&Z`EGa*?|fox`m~+HPd@Qd)$B)S-ouzxwci=* z8crNQ9tV)eDP22eGAJ_LApwjj-aIieF}QVNR^imH3Gwt^I}-aDbUZfu(W?)PoJ>;g zN=)2Ybs|VRP)-^mGe;X|B(dMoI62E_X0kN1cSzxI_j|PX<5wmo{`x88rF>m6?Jsco zoHjE%@yB`~-oe)az6gK(I`B700N)7y1_|J!{nJ4L_@2k#AOUIw5}JLGaHX1YeH}^i!pCkHxn>i?#%K(7b`! zm}%%NgXR>0PZ4;oz!wUvRzOw=OiMDx+$iwr0{04hhQPNAe5Szn3tT7g!vdcz@G}CR zBk(^8e6GO%D)2mkPe6kkH1h>MPvG+ezF6S%1-?<>g#u>;zEI%11YRWY9}B!#;BN>_ zYqrLGS72JFG3H+ct{1olhF%6ugTUtqtcJQ<1#T4lwE|Nb8M9vCCV__prlkgBJ|-{> zZsYr+z%2sr5V%#~F@cv0{IbB>lTLu~fpMh&KFGhaIh{_GPpd_CB;74V?V>d^q+7r^v(bhf;1>b$}* z9}dF&#KIfU-Y=WYyk2i}i_&~2@Ke$Mmll5x>=T^?`88-3PKvu7c*Y%B3;Qb)MuTjC za2$le5q`G|toUCEyhI$cLCT<_%?AY5Hb9gs@4?_tA-tn`Jb=US%V6BZh1nj!VVLh7 z1m8L*iW`Q%=OFkoi$Cf3X{Pbys9f)WI2g2&en4QR2HP0|Yny}}49|xrAuebMAe|VRvaAyD}LHp#J^J=_}9^Y7VxC=7bsdr!f25137itgF9Upk9n{(z-@SDT`EWbR z?>@(qY9Ji^aD`XG9`a!-UYRQ{yKH&gf-A0Bv#_pl2?W+_x#g5#>aY$I@jQ7?DxF&j zYvoJLx>PELLxFu6?5=h$q1g%O0I3ynW_A|G8sf|1Esf3bmNpN>vEJm2t?+^xPrZmWB_4OI*miog#i(0?R%|R+U$N~(6H&O#EryJEtDs`riM-&|hl*_{ zxmi%L?c{C?R%|<2TS0mwqGH<#^L|>_tJrqpWwnZJC*GW?*me?gOjr)%-U!WQ;HP5S zsbbryV%v##q-aH|V%y1GXRp|Hf*q?4u_$M)TQ>*hZRM_LHr*vQ2)V0_yQ925no$e5 zXh=E@vla=(@GqJeQ?gBr{6+E*#bWwb{f>yfsbbqHu+W4vTBU}cDz=^4(#ef!n9vfUGSdw^EP{m@0B7&KV%w==+ewX|K>WcXKiUHfGmDjD zUM)wfs@Qh&mZ~C?N7V4zp(3>Lf)}kYV8Fgc1{Oy`D^6-IrefQvV%v$vgaR8^+K^-( z5*oP@v>M6=noVg+gI*QePPEK4kng5RbjmA;D?Us>b4_Zh-3c)HC$^UTR&myGy!!0u}|bImc<(QZz&maZ7) zjRs~)Wd+X7N*XQTWn&Dchuios zOmS$?Nv1GxPsGP*rFD+xrl!Uw1n*hbpRU+{sOuak@R3e2cY>q1*Tvhw;Cfg#fi;xg z^ya#X{fCPE2Qfu(IG1N|adPRDwszKPGk}-cwPo?m6JTLApGUWF8cf<7 zUOOiiR39Cwfd|wfGx&}=ZUQyPGXuG%X6#6I(iS`z5R(T+aQF;3RHNeX^rRj$0Frg3 zSbdg8gGx;)nlBp2Ba8^_UR|ize|VKU>O5jsvH$RDxA@7Siv5QvXD}7}4;A|lS@aIR z2)S-xU}^i|E*C2HA8_g#mK`uWU!WEqD)t|KtJYPq|B&IU$nsiiXF7A3OEt&Miv0&p zICJ$f*W`1375fj$3LXW<`>NnhdhKS9s@Q*!Rb9pY1MVMH>_5O%OsqxT*%MyCkW_1fOKR`i-r!i@l9eaKi`w#wNU&a0dbfFdd4>FAMQPPV22i*73 z3);Mf&BsB=lSVwo0h<_&+zmJ!UA|lRsg?geJZ(FWIznqnm*kOJn`(L$`wtcS50M$h zxLkRw*ngmvv|1;X6&%4*IQtBKz!T|v-i2a+b8HYnUxGA7?8-hpxY$YBqJK}{_2FQ28?GLLfedS>ChZ6WBgsyP`}vvj>QYld75fjlvY-x)f%C8H(!G|GoPUv%m}@|RmJJ0I1}YhPvN&Sq7>RDHMr&N1Af*nQlpRpx z98hENLr*G8nz2 z<8pI+>x5k&&CSPY^W0lm*xVSqihPwRV%*k4ob}Ms5sfIWY#m9Q`~y93JBpD+b391G z^1n%Rp|KzhKPtIINDop8czlhYkTzK+H<8H=v3}`Vxm#T3!pD9bM9ifkq*ByhLbcPi z=)5aPpYv!Igvz^84x=u2O?b-{ptMLTxT-f+m#x~Vp5eINJ)#mLGqCbY-&zr zdLkQWJVWXP1wHVmW03XG?C8T2+W#T& zO7P31^$UJoHsiz73_p5>QWqt&tWl1P5}kF2Mp<}VuyMiDA-<*FXPett;t?@8>)sh- zLM>XQ%phHY57@gg^k~&N1S9nKZp!5RmU?Tm&)Sa-P3UA{Z8S)A@FXneS)0o-2m|5*P9J9N z>O@$3dGio{-iR>M@W*?#dEA@L<999*_N%hLXXBUX8TjKn-ggIK7{rA;A28pu4#T;- zs$sZAaD+)Py&7Cbb@zLj9^>J8`jT+>gGRnP?mm|WZ@4l~_dSbne}Q|j?sMUIB{mHA z-Ya-NHu_vR{>tTN>$MsefNOy~4(=21NBWS3xWn{rdXtg6%kYxVrRUt?FZs=HhAkoa zs&36Wa)&uEyyLbD52G}}H^Xz$`5znjGCpf4`evXMA)Qs`XA@xvy4^it#-f|IyLrEu zb@6({=6zw_yZ(%I^G-F(x6-MPhMun7k=Vzq#*YQS>*ynieSiJbn&BS|?P(d>Q!`PhEiOW9K)~U|3qxC9 zm?+fjNIcJgySJbn5ACU5Zzc*a6mO=3f%YMRZKH+r=rfHzZ!Poj@mt|BlBk|<%;>}u z#WxHmisIUPsd=JUGo0A7Be9E=jUpmoju&{xo+M-sc*5;eEZ z-2JdPireRG)A0L7s%nSErfnOYea8}b35`gl#DBioJ2A1k$aIA4y(5XeAiGt_K1ks0 zWr2N32BsiDG8j&bDV4Lg4}-Sbw(cSM@0&jUK9tAPv)9p^e_L4Qod~mHJ|lpz854<} zBWrdJuOTN^3@4r&S@YcRnrDZ$K5Moddt2gJ1{+B{`^5fPVADuq{}W@g5Mtqz!#m*~ zpSeA;Yr6$I!LOhUpW9vEs+z2?n7l zte;lwdRdyxaN$eR66yi{`>iuC+Q;(6Ol=MHVH zF@^3Oi314w#E!&%{D9N@$&vS;3s!hO9{``FTG|rTDEqP76Z|SM(!Hgx=Y&Pn6$-< zB1Y~ie(%?%ggZv=Dt@EP2aMGCWT6!xXgtcu=YW%vx>0IRD7u58=DvuLk!YvCW#n62 zu6C&LxqiUq;MzCFK`&k3jnL7^#gRgiJ3di6Ty?x#l;=6xldCPu2w10xVY(J}D{2H_a zOd3!r6NT#HBW}Q`9@<(Rgj(Q(OcV|jKdhW7N#_{wTt~99PV`5ZV@iA6JG6CgQ1^Lh zF{PcLJ!%hoi{IAy&ZHU2-r|?be0UY1 z&*5vrb#zl#_a&`sm+PkatHMS{w6p>+NOGMh>@Pls`3!rTR))0a@sw5uN8;+@&6dj? z*~2y!koo7RL!{x`47=kOD7bYtu(vp(0gC?sQ53Zq*Ek-`G?~#6gGPg9(hbu{qs8CT zQGaOb0fS+1AzI5srds~3;=lFXbqqCMLjg0g0yDn@jKTl z;{ftFrEAAb21TYjB!Dr+o6$=Kw@%C|oZ2-Zp5Dt!p7Gf1N3T9GaxzJ|D=~3r)rlbO zKsjlM%p4CD0$KM*^|NbFNee&%Pfvijj zpz5Kq8QVtlr?5{xIrP#D{4U%%G+Nb~*qf~xe0kRtSatiy|B@*c zuEXffl=hFTsTp2_7A`K%_BCB35TrY@ra0}%;WfL461!@~Pe4nZK}kR-8qThfHG77q zkJk-t-DmQz!-N4-!jbE$hF4dO*Dzh{A~3vW%;)fik{mF99ZI}dHLfxSbJ-V1){G5L zA9;7p$kj+?|48k~N*1tKJ$zX;lSINRYKK=Xd(eOy+9#3%OGtXSO*Za-aoRAnw&2->HUcknS>5F8j;b703R%tauNS8yeb$On}F zSPczr-6N^)(Ny;^)sZ!Og({{7jz2M0GgO>Ayb3f+`lEAEs5J*3>_zntubP8EpC*Xj z45>?z@SAW(;Yy;xK_2>SewC!mQh8pMw}P71#J=q8@wp5zT206F@#7`h9Cqc$q`#gd z14ni7+in`Te|%0vM_~#&ARnqn4C;RSYSep8TWw+f$kiwr9U}%l(~;ZoG^vv6;pvzt zLs*;fD+LjS;8cGxqoB^J*(y;%-v)iOiOH72fKlAuI*$y5_`2$r1=Ve zAk*H7Vr)-TujW8@8RE&<#5OPi&&BwiUN{RQl_{Jmz3O;rPt_n}9ECx10_`Jf_H9q> zod$AH-DqvBOS0|{B_4*>JtT-LbXlumC~%$KcmIf_JbDx6fVMl9Cip!e;S^z_bWt{G zPK8O%NshPDq?9t(CW3s>5p3Hy6B(fnbTNglUz*p8Qtu#Mvfpe3wg!y8^%?ZueWU{sg1Wzws?1dAW#P|!|2 zy*iHxTIVl)n+5fb^I^(`nY*vlnFbm`_ahvIu787@`yx{eb59a^NeMDj-TeZw3rew7 z-Mfi>y~EaYAE0}>x}^vhB1J@_fxSsO(f;n&#S!Mas;Ciu?MJ4k6ZzL65`0?Sy_o1P zDVp`6qWbe3vlI0C1Lp;9p|Z=fx{+aWVXDZglgRgkNXe=|^c60>lB~AV%c_>7RFoFG zAJ&lxlMeNjS>OyIm;s8N0TWP~#bhf2;Z6Tx=$m z(U1ddk5EFu?=fl;F2qnUHhncm1YLhSX#eVBPG_~u32K+>;yId=xRt_M#dsmKS4xP{ zdyZ}P50Vd2b)6?eSpi9v*t=sz6{}+8-Fq2zTJgFcC|5C3URG^~G?iD^2nCQf5*-t}Rp_!9M=)m9 zj6;{RS1AS-bBq-=pCW459k&`tBYMKH+6m@>!wGff$v7klpwXy z@a30Q8sr%XB8uL9#XC{CGRE0nx)$QU~oBNz92{Me%bBs9oacTx0a(jA=Ll`7pu72Utp@pS@rqV;f$HRD>D=H;xH$7lMyS`uEvsG_NxT4o9X57v~{N`U`IiE)s$hEmWK6|T^Z$3N`ou^Zd?B6hPFNz*O$6E&^?H5N9UmQ+6CTr}+CJN6^ z6rL`A?ii8$BDW91lPi=48)Ql{=amGRvbjtv2{L6}f43-%v8A{c>#E^3e-0H-xTgU3 zpL647&Dg5pHJ=?xe0DhTnW3$pk(I`0bWh00@8-EL0{cz#|lrl{4 zfsr*2$Qp0gis8g3N7j6Dc+LGoTkn?y@7DzHmjuyIcS(Yu)CA#vY(`=<>a1`*?)vNa z>#Zyqzt{osku{HPPrNv-c6T$Ch&piYEAIZ06xzD?7XMD&h@~C=5i1sJxg5Z96w5g- zmgbDCnTw^?@ESLDockcB5+f^sJ>cj6vb8XBviM*`T{S0nJ~G01Z3l{NI*OwcP9Hx( zb{Do|ifn4f|0ax-^^;;OKrv~?kmz%@!uo$YeG!Kb5!R?n%tp@EX(qSa4isPcp>6nO zlJr1vSD6pmxyJuKm@u@p))cUosAa1j+B(PNz4w#!=D1cHo~)g+no z#wTxkNtUB4s}@2L0_26Y!#ig(Vco)$c?0+=eFTi!!~MiBIZ0OwQbZ{z#EoBptkI^h zip3HD9tgj8y(u;eFY4^*t3gk<^?RWBOC6hwS0Pc9QY*Pg<%Z~dps=i3e+*`T_oFt8 zeH5j-nKe=5o;DoNm(b5tKln7p(NBLI@a$=45M^76sxD7MLfzb12k)C0vRJ2yjf0k| z^&$eRpBS4lVUsXd3giWe@FeE`ogi5P2qjp5an?$=rxN)Lpe?9Rl)LnIPyCPAZ?Wcc3>f{8nh3+}x7G`&iwAWS*K6g{s zaQmrMZf^ILI)r}=vl;V~uRIWeXA`W$KR@}(g9H}{C|Jx692Ug|DA;KcRzbl|L|6fT zE!=&KA?~`m`ftZx-r05b%x6KWBm%V_;rS928th}=SKT!g3(dEso!Sum2AduQV1Xj$|aYRBvU zhW1btLdv0W$m*fix7Qq)#$G=ovHvbZbt(46 z#{W@-+FnfKZT^ssOyBDeu(84f=5X!F1nx@IFfR9EUqald?;;-5t&j?{zm~vW{brpY z*i^9s5M@8A#T4GS9{m!%d<<+Y{-ZWNh@IUtP_Yr=P$mtwP!ElrUs8%`B#JP{Zjy6B zumy5aggp0@^Vl`t|MJ{>>GA&QH^QS5~wX2J7v)Mce89%L->a3deb;VUBSeB}K z{A*K?aIOeDaISjx*UakT@nz9S-cHGR^W>brYh`n3&d-)$m7iZLt&7|Cyp0UmwhKX7 zt2?%hS?HN<-y_7D+V(sXVp-=7eU-4;GF!bDZx_Fp=aM^HW@~JA9c-SyJa@;ooi;HW z4Qi3Upy#$C@FO3gFBH2Uc}`qRc>C2g!`mMLFuZd_ez|FBUTzrvh?M24LR|i&k!^I& z#FGl6!`q$)IR5$(331z#D~S6DaUg+5k9_1+bJ)ggN912E@OGgd`yH2} zCY0nEf_etyozOm#I30uBwigg_=&StKY?-w~oTOR?((3Kv27@%f?0fEj*Wg!$w|Q*9 zqa_QYR~JvSD$ggkOKnW6wKbqAOjO@%lR(3}Y-sCTlgBW_elT}+@q2bh)<+#bMcdvp z?V&IoDv=;ZLMq>1XGuS!*JnLfT0;X`gnV|~xT;*CG!$VRIzCwoD8EJyb2 zat}+k8UNCVFvrO;+l``)C)=TC?*2On{$~=uKwlRPsTB>FvTFYt+CM)(bN4+6WV`ov zZ1|$F(;I>-@gp$yM|!?#^Xw142jNE6U;*@zXX~-)I|eC%9fRHP)#z;R<6oyXzEFiM z>$xGJJ3-s$7^Z_wprK+tH1^{k1050^9T~dxlh93u38rROQXD^V5=sWvJxJ#l z7)bZ8pSFG$&sD4t7S#=-$M3_8cMPfi)=3&o^2f3r=#!MQO}{ObW<6#D#p1_HlU#my zRAL5LEM8HX-RFreWL5~1?LHzNwt8Ps;dG<8L`uGW&e2Rr107|i>r&5 zm4;l-jyc~QwX@YXK2KZJI0b@lqpOR@mc*4Ny89G}2@MThg&R+iJx6Lz!Jr?nE`HZ` zAdcZOeCpg6b9Naa{lI%b`%C^xjJz`vH98iyAr3ZHrad9UaSev!Y0IbQF}gGK?5|;R zz_94|7$(>38()Rdjwi`DT0i`6i^>y*U^e<4mOFNSUgf(gva=L1Z>aq^vmjDu7RYq` z@0BP@1~iKOfJ$TIxrZ*Hp-ccx=qn(j{P&UJTW)2V)@F~4(`nN`TW zYMN2*I5+)Uq~E=6ZSMx;rA1R0Ft|b4SBB*+7fX}|>nG_sN9W>8>JIk)@7-9s$3f_4 zdzIR_sl(~-1vo)|41V2*aOfoy*Lmg{z1zq)t{qw0E{ z@?h|Ij?*{HszpDCB-nq~>+h_+<9}h^eu~H)kwEZy#>^IQ{O{b7U})D~S>-Hz8p&WH z^X=QVRwJtm3u`gxj+mjbYMf%Ykudc-`-;~hTgYJQJ8seSx^>N&DU09Yy(l!ql|2T5 zT?$0oKa$4z7A}9e<3q7Iu@ej6;zh`C9Op}<9N1fm)CJurN%4=NJ3ug5ZLg;QU0uAd z47GQ4@%?2e3@N>3sIk?>w?`=IHtuHnIL65IyFWfk9Yyy9^fHo36Gvg-?+fwB=?shi zRftD+GcEq-As%`D*jW0p+r5AFM3WS1p#}y!uz~H6a{^@1A-Ud=;v?jkL;kZOp@Lx% z7MKT_=%?>tao$QVDD`UBS1Sw-Hvs3xKp$tvOolfZ^HlNNAdbLReKEUkwCgyQ%gjDU zwK}t>oy;61F^r(yC(o!ge}i^$&kOgA-ZOSj@t&PFAI#R~P(`DA<6YdV$8wg6o1wAU zPmEP}9Zyxts+od@f@GBjs6~J~xw($Ofal)1>R3lOS|%yM0U@&FeKU98jUTjTs~Ew^ z)-`kyG<5xCxLa$do5I^mkb}3@R+&Pg1lKimYwb)^s4qbx;IXEliYl~Q_)*P-Lkq<( zXhR>LB|T|3H;L3YAQ$v;z?Bm`vTJshP8G1#Hr*fk!PEoWyPTJd|AS0TvG2!>h~vJW zs28yBhf@YXGcjVx%i-xRFXBbv`d}|A2WMdB>@>#0wR6zdEKG%u#S?Nt&;i(H(C@|! zwtVZX2hJ(2F8(dOr1y>BY!%5WRbGnDay2|A>LnA2^hZ9Dq$V4@6HTP8IOM$~PB#Uj z^#bg&VtJ~ez6h$T&GB^mARSV#X!14QfPNPr__; zKI%j>WXq_NqJB5LDNp=oC*wC0fA3`c9mH>*jK7okn@2TARYs@fSh`6Z#fF@U=nE6|jogqD+63haSe8oN!nn6e*eD!<)9@{>sxR9$*z zUxuggFEcx2%wT#})MvMX*%eU<@Wh5N2a|(0$JWGtbg$n7H9|@eG%ZFuZ49VYW?ezw-3%4wqKuX5jqAPm88iGkK*VxMlT#EF@rL80gyh3W!Zl=o$VFb!rHpE z0|R-Tk)I7x`ADl_Rm(-f75H6^KQPA(;_qJk-Go2=Zs<mAPEH4{y*|-Zwk&CEG3JWPE?Zu=;EHS3EUar>f){I#wAR-wXH~B!G|iO9urq=IQ|cqX7PC0H21o;KE-O zz_|eaWB~tV0DmWde;&XWqPhAs1Gp!E?+M^91TaW5x8v`k3!!TRo-n@?sv?-~hxiY^ zwE=!Kfa&MLoPp_wODDwNauECr2f@dWnL3^OEWY(wV}1&l?+x>tiABqxIYr<%3OrZf zr2_NL8SV`W%p26kyhGsA1Y;lqN7L0^dCXFB16A1g5Bi7!i1hz%L2BRN!9< zTrY6#am<%f4`a>~m^ZQ^Co#Yfm~6Ysf*oRm^XJz?TYqiNGrbzCqwC1m-`k{j3L`?dO|#wD}ncxffpF9YDKBO@7st3Dn13YN6`l|rmAHei>bY293!np2pc)A^Ye-P&97T$pN zeg#f14dNEI`&{@W@KdGv6^lRTG-FPwW!!}>s?Y29foI&2wZ`m~FdAgwM26S4_W^+w z|BAp%#BmZx8C0~nOJHpSL^;|H27emi9nC)t;4pmKNl}>J58yD&KOF>r@ti1b82-M4 z;CEU4Nykq!%WzC}kdg4c2jXDRO8T6@ivN_r+9tme;GcoW7&JKzgny^v!;1*x%HD{& zA$-<~X=WJkppE%Xh#SZMtpcmZZh@8mH4sBCd_iF4|EB`{Hz1w{&6N`Q2Lfw4zXwq@ zXs#7UT42S`Jd5~usssNz`p*TPbp8TG%S#vy@=pS%#POQ|pRHp$eS-g>zyg{RAPNV~ zE#f#wV8y>f;6ZiZUzaZt4!#*-NW)z4>B%5&-8oECWm*`%8F+@@v;wm~3)BDaEc__s z-vtoCgXVIg%_Ra)mM!#qi2vZbPQobsh`=h7|0ckv<}uAi4TOJP+*^RZ9qy+fa=$5I zG)ODN{h+x(9M=e}_-z6su>D**p8%fexXG7aBBafg8|t2F-^A{*J)+3!DS%2h9rt|CPYs75I$}#1{ol3;Yv- zpB4BOf#WfTIc7TZ`n14}0{>9p7J(O`i42+^f$tW0K;WMW%$WK;DX0FUj>23$?zIJn}w-VBBP7|9Z8s>Bxo8iwiZ9rxu%~K5(rnd@wn&Ek5h9AY> zF$D09;ZH|t=Bx0XLc=-cJD3}P81Mt8POOhSAn@5@4+KGx*E#%=uic!B&<;M&&cYaG zp+U9D*A7T$8N@F(+WH7Du~LojQkjo4LObR#PI|S&s!U3g0X}YeJ=|;tJlyL0QaiZt zL65)K3|6-L&BUNa=z#AoYy&+^E$$u*Kz*l z@SL*|=A4alGs2vkalYijaK7YV&XF9&-JBj+^g#xXJv&!_7vNsBpJ~aBiaTYd5VX=Hcb0&BGU&goiINZ}RX8^L7ufH17d? zBKW{HDP`~30?!qB(jf4uh9_TF3VfR3^={@nia#`2`Nr_?O*Tzbfxy=y{5(+i_;klx z$0^dB@NwHQ7eW)1uN@K=&BDT01w4NfB(Z)>e<}VtCHb&C z52@(aZu)~Ucfhykb|}9b58KVnZ2I!Go2yi2CK&$2R{#zz;y0U{=~7$46wYdwB}jFq27z8r(N7I;M9xh3=7u#flJxa{MEzmD+p%!TG& zl;t}@{{>PO25Sf2eVp67bU2@%Z9O=jryS{rP-9foQL7q0Z- z-;7aCzjmyzIU;F2t~zL025fkig>Wh~}JiOX`-NVvRobK{{x0&nV_n5OhoHUC)++i;F@LF@Vhg0ST4|kfi9$s&F&R@&5 z!QAfQK6Af^2h5*(IAgx-;hW7<9?lx>!D>2v=AS*h#r({}x0wAN9yG7B3!Zk&$WQk0 zkl~)KrgMjhd3c*?_3#JG6&}9R+~nb5lLovA?RF6DlVkc_0-qsc`bPym^Z#e>UEt)Z zs&w&Pd1wRCG!otj0U9JclJ4s2PA3i02~|K)AOxZ)Vo|BCevt0!q8@>$(T3n7G>AIR z$c)Yya7M3h$MJ#u8Eudus5qj7SG=P3DB~1%#)+D_W;ELO`_|ffpIzM@W&TO#H^Mpj zReGJZUu&8h`53LZ2*qPs%WkpPRcUK=<3SICqe~ z5Aj>f?dJW5xc%ns2+=ET?g;i@@A9XWOpBuh^C;4~dFO{h>mK13LhCN;w?ZE&88)qC zd|ekEA@tG0KT+sogyw&or@jDrmVi(Ce$cgmelE{n)Wa>%Nf~N) zDW1RBuZX!mcW1k4^ytfcoKuZE5lKV3YrsDVGK``uj^!mnA0=b?e4&q)vCQP-_=V$? zX?bz1T}J{J$HEDC`N)k6(ibBi$A2!53+jR6Vj<3JIW8Wg!f-to&YZ76-8mN6ABI4? zu}~BGzd9CExQ zXR52)lgj}-fiFfRDqvR_HMyj_wYx2ekfD)c|6-RBz*897zGR;n)ry2HDfIdzz*%9E z6fz7i+_f+Fmn~*SVt}VGamO=B+#o1`PkI2W*Ui+ONZl5e?dk4TFF{CHX(YoLA_2t1 z=e&G|Ty0@Kp6)il)GJrLPJpu015_W8t_!+sPXfZEoBu=M8g)jwfYuC}AKL zns;5hmg}+&i$)u*OLarRL9sLBLnEx~=uS$#ve^x_!J+D45tvE%Y^h>v51pS0S%9gM zT|F{F%qj(Jq|VMk_WLz%hXyw+!|y=hG!uBUw@_LJ?4QA0aX-PIDceR0x6C0+>$I#{_ABbI8ZDP&Pq0H}b# zg#ixeq8cDPVHYv30DJ{g1NI7Xb-DqMU|pO$z~lJUh=fMtoM1})4l-+#!EPzPm*@E(`l|byaP8VGeep}D(vb6{TeDx3? zsB%_nK%p^z(M4l`){+2F#VR24Tzsu2K!8P_AV6!ne2FO+`eDEdUNLQ2TNHtBGVxKU z&x-XW;21QCkWdqCthHj1nS7`?qSZ=UVdAisv9}?dwPZpdBli|IxuTM+rOglUS)R>0 zT6Vx?6&<%Y;v=(ymOTvUGtX!BEWg$e;OYnST_l*NPZ87(NVksNZtDh>1Eb7yiATrm zp`|iLx#!!OB6g+FUwNJ9yQ)G1fE@GepsJy!+qFHJO9NY56Kz%<9QIXh*Oo-ieZctg zS!=&Vm#Aj>^|20;cI~*3dVkiI`=|3!<$KP#^~qpgigOkmLh^%Bi4=woPeozVpe zQ!(t41dtQRH!#^&8WV_x3cLocw3M=mj3$6@U_11-b_-rd;MQu^L9r#tRm3%L%b^Iw zI@ZY4uVjpdK0BEdggL~vU0*`b?*;x&6Koh7$#1M^0P&H#1QrrWa%>dM3Zl1nxDtjN z`3jMY`1uAtA(BzR{x2S8ouDhV4F`1=#q6KY4IQ-k!e*Z<_aC&$l#PL|%RdCjMDOj8 z-rH5(l&uugLxMQ)|qi!OpzjF#(d0H9Y>orKvu z;3~>5%X2M*A`e2=Fojucjo2%*)sc+_0?q<=o%O*V_OQ0r6pIgcO15ht0(z0dmBe-# zj6bm1#RCM^I%w`{4!Rnlq*-LwMw>curSn%F_YBEqN|kvp}0Zyob(V3 z`!N-aJFV7kSIM@pl8Baa@*~pODMT!4_RJ7d?U`E2*`2L|4B8F7YB==ThNUC)N+}=} zWK@|j<5p6w3f3u7VGL3NNtf-zmXK*M154h(!L?I}h7b90H-QwHGm~I?mL!B$#S|i@ zUuHixavK)BA`GVQiYm*hNMLk3MTPw^4?ytbGQ=4d^069mJT*;ZIzhP*m0~fa>EtR& zte6P3b1PLNZKTow|5$a?x_@KHZ5ub{HjmWybI`cF4#d3X@^ZUn`}Y7z=yJ_fh7WL# z02j_499a*@&ULz4ON0Bnjh~)lHCNdvW~5rFaD=R&b);G3%3g*6p}S!-JG-;M;l{Mx z+v?d^aeJ?amgQ2-&XJG*;>s6e6)qAri3 zLWcL|VNo+Pc1~HRj{hDH7S{BlGp=p6qJyIU5Zq^DTTQJ$=l>!cXb+hikwjCaLJEo+r z6`fojkJPG*I}SaxI#?YlES4GM&_m1oeE*ET-qt0D9=fqmhSZC>zNifqD$AlLAcr2R zyeLfL&R_%adIoQ)PFDFo_f9vT-Ne^_uLjCo;-bUY?L4r?vpCVt@{q}e=#Cz+Rge>jr%n(FhenYO2aA0psvfL;D;-HY#YCbE7lv^-dt<@1 zkIF-CtyJ<{)seIcj%Nn*vXwzQtD5q|y_m-xkCQ2k1%`434>N0ch}qC5gdeEi9`V#x zQg*DiYi*sbfrTYjjZeXB^@WMmI1DJ5-7=q&8ipywHmulR9k2!3qa6td3T)TuG3;s7 zlG7qr^pFHcv1*J|dpt3Qtptk}7>C|LYm$jx?n<@iN##MDcx$ZR+rzXJSGt@)qj;2O z&~~sGVN_8;3T;Wt4x-!EblcZx zY(sxuJ}*7SAe9G6_iLl=mXBi6mzAIaSv<0Z;TkbVbzlaT7SVafxD`bzy^^6a#OqEFPs71Rh;iQlQSczdA z;*K1sXU{sy(7g`Ou2=Khq#5FT0uKtH2&|>iIiNW{=_pk#j|UdQt7M06#9=-g1j}U( z?PSzrWDbC0i;TetI$jyk4y@i>H76k$rWk@Lj!gYy&rjGUS^{KA$sv*ygpMG-OtoMh zBPCNPB|M%HD?7VcaStWfgLO1pTaePTCMn4dYc&Q8aHHB+M-G_~7t@+uIZ<4dOT?8) ziQxyZ4-NLpGoUQ~vhvGC>s@%HRw-0xu_1U)HjGu>f*mv*vYp*F2bdLSDriGw zcf?w;IMivmjtiEGCIC}x#d2B2{XSbk?b#tD!ojC=w7P5zWWqYV$2Sg)$djtzG zScnXkyQ??L_~pcaVW#Gg0eCXLic9_K83^LgW)S8n4Z6IqPeVABHiTSx<4BoLK9P#! zmSJc~P4!A2#Dm|4WEba#Y>u1~8H9BQQsKFJBnF?lG6-WwgS;4=tQtbD9Pk-(z%fk4 zK!$;cGA%U(Ml#C!jbU1ACcG)C9OTn%u|`*6t_}j7TD+uD{g9m|^x0lf!zvW(b5R;! z#tQ0>X2=YKKhUyOJefHZ613nMKVsk=SM9{mD?xdVfUc5MJ1FIruXVwFN5 z<+J~UCR5|>!1*2+LuAJ(aUq)OmYX_|VWoh$}}XmTOzc7T4H#YMl@i+7IOK9}{Y;1}flGL>0j7!XO}} zSAlOGs-N zRlBJ@xrW0+t7h9F#dhe*MOMbuEtkcce_B`9RggtzD#*g>ze5IXGRVV^36}}wK`ueZ zz0A)99ahO5RB~IgC__J*Kt+#5yogxIM@*Hd*dVozeb-Hb{O!o-;K-PkzZbNj0I_c} ze+DIx>53>8sv9ae;qqP1wPGG)l8>^mY2!Be zO#DHo?b8D*`L--8=tC< z7z8*Y;EPJ8QVQQhGWu*4SxI8P?j-M4No;HajXr)O7k%tZR*!pV9QERhH~g=8IRQ7~ zcu2b|6p#1kd2i3;5a36duud4ma8pMN-!fQ5(vY@^*pM@Uvv= zDFc)NE)6~_$LG}Ua{1BiQy8xgeznebk?_~W#L5q2K))@XRhJTCU-(vVTfGRrd zytN8)_6`L(i{alQ&6HyJx0A8-r0<{@{*8LT6~n)&9{B$~mW<)wT0|2u{F?&H#PDxA zFk<*O)k6&brh16s-(vW;82(KiX2UK7Ue~aPY?!@#ks#CsUG5i~QcpCp4qyb@BWB9jdU-R#HUujg$fH@8? zJvn+!Kvx3#eF6QsfF>N1OZ$s}CcKj4uLW`Q>oySw!z#`HnVokRjZc0{K|~A zCXr2<^%Flj@InCo?5CH%edc~39Q^My^BDeZcD$e%{>?(G!L0?p4#mf(z~b>ZZ**88 zU!2D9Z*(32za9KUm#(s6_%~_--)iGS{22ZX9U_K*BX$8kSH~yp@ULUN-@!*y4F9Gp zi)XODh~eL2__tZsqt4NU!&i#o-;y2~4b~=d85G06#qe(eI!7)tzlr<482+u$8^gc-VfZ&X{PgZ02?<9hiR3wtisZGm0_qP;K|7cI z791qDwJO>n;q7YH$s!cVgxcCy`$m}&X4xTW1E|xbgvWv+wJF(Yh$(^+>1`zrh%mq$V2?M z!3Fwou76O#uc00Bl4%vjV-GmZ6u+4R1z@CmY%=wYX<^sIb01F6#MLDu4~^s+k=)cX zrm!u@TkQWU_Wy-fRAT>M>$m;ORIO>?$hpntYh#%OZ+{~8{}ucHf@8?7Edhm`S=)MEVKKLMQ`SdW8O1b?EkBL zG8Gd0|BBuZj{Sd0kC6ZFv1IK3t3@;s`~Qmlf8F&$V{WnIBKH5KiirJxsUBkgU$OtM z*#B4T|7#_HBt}i_|BF^JZDRjl&jlYSvHve!13X*5u{X<&X)#Bc*Cd*yqG{0XN}Ce` zzZkLquh{=r?EfqF|E1R9xiI&|{=c-XpRIFW?EfqF|HU32`~NcLkI{ch?EmZ8ZJ%TR zU$Or$wa>BtFX~|&Kj)`%0(9*ED|){F$2fn*{=d+PamD_>V*g*EU)R|GSM2{Q_W#w| z+P>Uh-9~&n$$xO&g0FG##Zc`33tDJvUDAC{Te2(m{}ucHT9%Iee-#dz=dz)};lb)b z%)L_G&kq3i=@zXL`~Qmle{C4rSQ&g>A!{|!Mz3f5fF0hg^8AX^1;qZpoF>|ns-oEc z7d{wn(+`F_I^pc9x3EcEU?tn_H^bl!7DtrV={_33vHvgGKc3yu7W@Co){3=C?EecpxdK`U?C#B-FFENFozHFs{Q-YCV*1Ap$r0S48V$r);Q;Fm-Bk;pEL!8P$ zvG=BFvh1rMCla2TQA2QH=X(ftrhOx-9yqwGbRbCsQ6-xnso{}cL!S_SP+xn*Q(H;d0pd_=>vRn) zEF2EO;@~Nmt-dgE5~a*;ISXR&@Nlk_9V!(2s{^*cc0JmWfS^F@**)xO)RNO84^JV< za%gd+x8^z*p2cym4^|c)3d^oeszNZ)%U!AV#Hu`4?6<7n+rzXJS2`X4N;hvs`Gw&T z3=-5!TM!OO&#NR;L0^{QH{>b>8A)u%1|g!5fx&*K6%|Gm6{OI!I?E2C+t#dZF8TJ= zqdMr%%jc!X7!>J~2hxpV2^xT@t56f!T)B{=I+EB)vqiLFF)85~Qq)l_<@yWu+z)+6 z`*URg>GJG~cKT8+6bmD~GNm9M6ubvYwcBxC7Ho8!QJ^vg)_u;Ndt*k#U|B?22rEZ&fAX@a~e` zK(;m8)}a-JyGsm{&Ti=l4eYi~&u{C@CfhYWj#iO>gJ(@-k&5z2rn2pAJ}%B$yKta| zlUzt4q7j4zIbiTq$SkXD7*dr^e}GHBx{{-BIy|U=G(2v#nUys+4mxJH;z0JM{78{c z`e>Vq70TDHJt?Us%r5*z!2F0^@6c_iC@fb+oum&rRc#vVE!g96EP=E|SqsS>I%(I5 zi-qjh$)DS!*(E@32*=w8c2yyR?7%rc-K3+onl0f$0TcoKp*5k`CD2Z(YI&U1g;&WA z-H5|{Hb}}gNYrCw4nX%PG6o~)cx6O8uzGXVoP=PQVhE-%I?#3aCukEb0kWhxQu#+l z^FY5Z7()vd7PnapC28p2Z&utx3HD%Zdut0)bSNu=_+hQafB|k)`|4oWNL);7cICt_ zq;iS4GAZHw_(1ld!CsyiSF-p^tuGg?ca8M*RSMNvYzT02hOuhX-tPy5&I@+XV5q{_ z*c@P1oT;D<>0%IT#o|z>&d?V!52D|2|tm?Risyyu?&2*XCyG131V^g9mnm z<<#!v#OkaNb>r!QeKLk;S2BH-3w<(AaZuR4UD7D%NDZ^r3OfpzQ!r~Gd#HDX&Q}aO zAQ~Zh92Q{cFc~a&S8tZ_%ZUNwh^>$13If`b*;RPn3;__tp{=v64Z6IqPeVABHiTSx z<4Czz!#HjkhL+R}U+IH5W*d@SoEx$^a!O}Ze|gOgQ5$dv;= zL-z<>#XyFEh%zlPp%08?l=U0K@MKwwyYi-}a$LblkyxW^=+!}>Q;U~0svok`gg)CV zYFLG0eJ-kQbE?t$qZu*-t{G7A^Vl41x&bI)0Lm~X3mTZ=A{#Z}^vnf!R!28t(lFt< zErxIb%cA^%toAM6y2#INDqx#6Qe4Tx&{wS?D`07it)6gcHSG2;TN;3!5H1!=cJo)r zRvNhaEVq!gn- zpQ@XY0v63`)#+MNblJviP_~3%pMnIUZFKXszMB)%PPC072H)qUsP95%v8<&mRDrq8 zbvg)(epJFkTAD0L>nv<3TMC)FH!5ZO28Rlj*GW{{R+0hsK>=5HM~FO>>&G?(9e{DQ z+hhAOgcr6P*PnT)EeI+@Q^2mn{Y6J-P#Lz2txZ&bG?%SSP)D>a)XNjOppJI+A>62v zQI~0`B`lolG^K1*O|osT32P*53=`z-+ULrseQ2UsB`&d2t(AeSI_g*bNR`0z1_wIR z_5m}hD7ZQWf;+Q;zzk{+8SUl?d~Xlf7WP$>0j%-(vPwe>A+8*mSgw1~M2l-|Jo~1} z01aH2zcoMF`uzCo^3WL8Bw5P!$YGVd#h5Ve_zE8(7=euE#2P`^KvREu;eY z*C(`rY2EKuTb1o`Z*Fs%<(PyMOzymC0wza0PSq0agtbxl(4UadRIpHMl z}sU({5Mr9!@$rJ|H+g{paDp~%#}1b|p*KHH{21*t*y;Xs4ya0D+? ze!Ddh?KBZx<*><}2FsCFz*7CWfr)%_HX}PQ+c_}XlFvA&GW(t!dqI4^{c*iMlGAqj zc6R!<&W7w!X_=QP@jM?u0!LHo5-FZQv1=8#U?%O-6|(j6)uT`Ve#OFF6!`iEko!l(jKOH>+~ zrUr3bX-;oZX+BN2*WRkyP3_4w92U}FQnnpZY=^E~WMy34a#_6jr*(B*1zB{af-J25 zJ7mx%gFO70aG6jZ@s!F;${sgVZ|qT{j8x zwrBPOXIo9NsUkLeKSbV!|HZ=r93NE|% zOqr2=Y{_y94g5g8o5Fw<`noC;6o+msMF-1#vT2Ks<}M7fur=DGIrt_l)_>5t9DwxI zig}DldFdAaV-%c;Kj^f5dSGSbF$=)dN6ITNv@99<=sH;C8ia@%Ne|}+i-^Q&&kS!E zF0L%evlcTf-Lr&zF$i$9)W$Q^N(mFfpq}91gmz=0T$Bg8l6ZuTz+*9qjZL7@XIbQ; zkDbX5z&$ihb(iM(zvkrx+=$~L?XFOK7c$4aJ(H7r9DX%~^F9n`hUJ2Y=dvUVBdv-@ zYj`XdatDie1~W9&z{{6v{@#^EWuPLiD0|C%`*Fktwj5}=oN*8JNwYu)BDI{SlZkw= z^X4&nbf+sao2gx%F(UThEP|(KQ4t~t*tP`9eevwE(vJ4tx@MuMzfaDD( zTz&rGL>?NfxE$GTd;nqO0ZGZ;l*)XcTLYvIS(1-78k8M%80nANVen{*&5y3>I|UOK z3pegl3^ODvQc)R%8ECjeVyU0@Nj}Iha!Q<+Kl>60(}fg|9&812BC?l4dj)w=3YsU) z+Tw&I$O< zMPt??VH0@A;m2}WXR08~@46d|`Gm^^VF8J|#*2F7HP^bhV*Z=`9d-BfskcqLpC12DriE{xcK<=#6H-jCg!#m@@Xt=Wzkk~O zKTNy-N8Fd7e`@w{)cjlcI~3giZ`|LGduo>9-^1T`qx}e~@FHmLC;0pRLz<;;@O~HW zYmo1gHk``+?VM)ms|;U&`}-09Ivaij?pqL_dS&>jxE~10e+llpp4TkyU>M$p`_Ul0 z1NU_AdL8~U{H6GNQxLuy_lF>VHXy?ib# z+~1G%%k2F}aDQtMKYFoeDL{(NrR!gQ)tc3pUw&0~?W&71*;OxJbJ1l1^U}mzr}o)CPyD=L{BKDvMPjK;j)YSr+2|d?kB*ZMfuCEq03cvzOX>#KXjk1#C&y zI>DkBd^<%?Uq{KkUHsYEP*}lgj`z`O0yFV|O*B~L6fQSdDw=Fom%lu*DsQ&)k1VJC zDof;-MF~=Fl$3rs3Qh&TR?6?!LlQ+7u?VZ84WDb*w#@Fa%$~8H1=})vCcm|H=Ys2- z>vMiRH3hJ{ZJAwTnO$!luPxY-**S&l>9;~VW9z})J!X2g%|Ymno_Xl-X6)KsOq!^# z`xi}mLDSd;ON>XTX!C_BeRDMr&(n7Zr!riR9_7A!ZFjG zX{ybcJRCGCNDgbdck=s;Lg99^Vw3B)ZrNk1FQg{c?;TsWVB6XSlZR<+D@fDz3EWeZ zYqg5Da+>}~NIF&9TYvX&L`-B$aiAwFU{ix+M>A(DnJtAf{n*L0M`Sug%5M^}_so_L z>a|2=lK(tvjJ9h7_lT_#)mCrQcP**u_HI z>32f%;L~&=E3=qPx0;?K$X}fn z*_ZMoEUCsaPx)#-5mobM37YUhk4Hg$2(lVzOloDj#ufM2)-8{jT9;M8tZ|n$#`Th2 z9F@^1gRB43w*HT9-STKu#;kF_))?29@f1wjSY~`~=CN&=N5(Su&&@o#E%OjK-I@C< zFSwVhk+U-QZ_9jY@-TZdj+?Q}r?zG81w!al?cS-{BU819>L0}_Oi)9cu7|;zwVoHL z3{(Z8%qnI}LzKpDX=|Nmh%#$i|DZ04v9-8jY<+#(`j3reV)*A6{u$jJy>#H$eojDWghr^@!`X;3xYOc3|=cYuTX2*QSHzo7s`l&%0p&x3N z=nEH*_Z`A|nT4l}D<^RzOB6bWe}1TW^3Xf5j4)4gfqGMUP>1r*!k8X7hlhu@rE8QZ5P)KhS4@)jUD0=EXKZkevUCL4xEw zb}1JvV?Fb>J$UnvxD*&$HE+pd^J(7KB4Daj3W?#LfqLD9#Vtlh{TIKLN#rYEj^UqU z_-BkNo2(ygcvXJX`lBG5m83|E%^ohJU6W#_@w0l4}D0$MDb5`CAPCto`DT zW&WbH_o&D*{4?~x@9PMA+-vDq;P1IsKLz|9we)jg-yOq0$MDZF{BsQd+}hpQ(b3+~ zZRq@`(7QMf_vCO11MdcKt<+QL>MkoC!#|G25#+OGvRy&Se#vsNr!dfai)gv_#?JBaE7rJ_d!)SDtExi^i0RBjKmKD;Bg26 z@IZuY;C9x}^)NsS)_z{3^-;FPNjuVfYYb@oZ< zwoi(HQ~G?d>iMiMy-lIU2%&E|AH-TS2*t1CvW0D6`mQvftNwYg5x{z06>8ZDkZQ5!5MZvN=lz(;#yS`7iT7p;h*XHzkg)p zzqB58y(ETz=0vVOVPp7bI06JT0v1WFG5j-46QSk$;AfE-*jSmu=OqZZj#VGT3>}I> z1H6|~t_-iFRY}HWDwD%y=>Aw=H(&*8YCkQx%L^ZG7DoZ*F;XO zG5oV^pXevvqOMd*NpV?957G`kqGDMV)CLayvlb}0UE_*<6DXN{*|Lz_LEQ{mU)W?> zoSVRhFu4}zCI|1fx|@TQzzPM>H~E02unYx)g}PYq-_2MXA{H@Q$f`tz5}83_1uUSh zNG$cw%F4#@&%RYx_*Px6&_P4J@B{363LTUOU7)Qs3Q^#oY8lRtU2M87N9@57dPMe2 z1=b-(TD3wDucRP+@ee)e0*s)tk>I1;vw?@Rhe`?#yx|^Ovp{o6`$+~qu4KdG2uWhI z(Hsqf#eq6_}_C?S-v_}6bJt(QT2lEb%1u~nqLh6OwtAS3fWx$ z8N9|>G-86e3nm{xlo!;8j+MLX=UqSd`Z>UqZUK0gh7N2exbdw^<$m&aNQJzB75IU7 zFE|m z03SU)=$|Z_HVObN+V08YQB(-r_;d$6Oj~U12*lde777>SCjpo3{-Z}4j zL(uQN^#Ty`P!=Lt4jbbYFbrU7cF4_UcccuObqlsW*bKOJrx`nO}0qz@tZ}A<=51&s$snbM1Z_EZGB0)!C6uvy8h1 zYoqvsHonDRl@!e|D9qsECW$8?^i5lDYBJS%TQhqQr1nBswL=keM^6)biil3M(E2%Q zV|=$0=Bs~g@k4bEu&pdbKt%Q}T1FU-z20zGjMhlM(+mfi2bNMsZf5T!498xx@YZia z6qm`uw*r#Th(UqL-9s&}TYw%n=?sY8qfUE8owNg?Ixo?VuEAk{qFHukEKuihHa>xF zn=nKz;H~7}V@*e-{O76iy_Mb@1)cMsqO5%zF#xE;ax~ZXEJFP_>YmuT2@qU1nS6Nor`po27Zyqs5<8yj8(O#uDv%uDsa# zPu5>0=@+f7fB(gj(bFqX2Aa{}=-`cq*se6$=F3~+%9HY+sAI9l@-aLfm^cda`~yE) za0sgO_*mv~XVbTCSzu}dYS|yKWB%qXd#I5&or)pp^}N@$I~EP7`|}l66j?P!**cNH z+T$=}K_RPiCehBjS=Fbxy+vt>+J@OnTRdx4QBmKqd2$SCj_*c}OyqRQL2Y%pWxbC~$q^1Yr)Su7tLK|DP25%7ky&uFY%ioI z!Whh!_wAbZ9axo`r!PbGZMUu0%NkOD5H`(^;ino@*j)c4);+VS@VHQgXH1jd8Pa|US!jn>-t zuAS1mcCW2Z>PBmZLW9h^HFMtvgoa-Mz1Cj`l}yv)m0NeV*uK~Y58|T8r?zf+O8Vl2 z^u=={MLt#kp)PA?%t%kw|6y7P6shT{LccWJql$bKJgKRhrFMp^>56B-nw3k*wM_znL^s-uuSu|d52lm&RX1ru7$j6finH{OQJ6O zRQ*dDp^i?8N>AW_)N^9&T3{^8@@d|}#ZTx!UUX zReDV0AV^33VORYdy55=5A9mG0J1xYw%wnW(XnSdyMLO?cr8BQt=8A>4eih2IgD7gO zpzW`8B|L?hH*EVaiqI~lX-0n z&csdi8?2T&vxj{u5c6YbL*(K642R<m0h!=5`m|WB>;| z&sqb&g2ri%H>j67ObrU6O47Z#zLrUw)RebBV4m1uSFe*HL0`JzVqr~tbfp0 zL0-TN*ACscBZEVozMHrF?)?ilL7Ii1U;n$iw__9f@Vq%@;pa2In>(L(cdSipe;@w7 zZO$E;-}M2U{p+rR=nHSlOfCHUk#`|@+hZ_viO04*_`r|mE&TlazhKIF&EK5pL;X?4 zUX*ck-;Vi=ib8it1Y_zqU<{6KnL4y~Y~Pdw`XFn0CX)+4kE4MjDavh`skhBL9HQ-= zE*cWE#K>AX8D|#x@O;*0;SQ;sF|FhEe?yxRNu7FuSzYAkUu-q8^4=r6nKV`eJiCAa zYT7z67fYg}IVK<6`lGq{yW}yP0pUKb3R&*^l!pI4m zqdnW!qler4j%vsc>;J8p`n*XU`5b9u>v!@DLbtk*W$v7<<4xNVlZ$1mjNKOY>p0+= zw{7jbN$gS)Pwy6tUAutEP+;@eDvDZf+IB$`gSjJ@%_n!yd$z4^#{bB4>qCpKu?jqf z6B$Q63U#GSj3kPCV9$b{wFA)GL-l`C>!}9PSuTVaF70u!vvs>85vsPaJ)7x{wip=iv!pUP8VZ4bqb0!ayWQ$PG$$Mqw z9U)UdQ~evh#K}dG93KtWC|08w={N+#VcZ>So6rC_zc_8|Qq+t$W5zeDoM2!_izP42 zu9>{p9)7qOT(qCVBxp09ILHygf-%WOSgwB{1FXUf55};(;AY27X*gos!`9f5*~LFn zKCKlt^;o1|gKFH7XpD_-1D*zOd;?b{GC%f33HAdt?V(?!=yhoL5xXM2Hi<^ z`Fi!e{R5IhsnQ`beR5jQXopUQw7-RhOHmSGE(%Q0s{-HIXkc#GyI_p(W6C) zGT7L8xTvK#M>yR|mr}`G?+(hrK(Kw|L==P;*KN_^(}jOh^aJ7LEOzqx#of zcicNdvs=d(h$gpcKj9ELvN2??>evl%oXO0^*(XN$Djc%Qo&`hB>8GJDQ(k-KzVH?>N`L z1Al+dTz@-)r_J?mXUbXTdPbGGo)M)SM>t=|Z0_^m#(RMCext&GSEF zN03yp`+xIQIxm(V+eULo{^&?|-hmC!npUH}NSQFET~TZBGe=#@gR z5_+xB7YWV(T>t3<{}$X6e(gbtXEQ-{soH?mlGGOfE@o?qMt|74u~F+QQ&wtybB z2|pjuy91iRF3&C;$jnw&80XzVoS#{m5O5b@Z9Qu6wBBAW&uhS+Ezi$cK4IdHfc+XZ zOI%Vf&mG`1E#c#KN*s-{5ms^3Xy3av+4e=hAMu^c zlK~yZUw%Xs=Z=65gCt)(9kTKVr4 zTKnYZ0{(+A8Kb76k?{X)dUyhHT-}$UZAhQglQ3@vJ!(_F66VI~f3wgU@GnBE{+nQi zT>Kk^R{g&>;6DKKG-@uE#Q!X`=5rBD)u_2bZgN5^KXDTI*XRxYck-VEKIQx+s#cOX z8s&#V=jG-<1AcKa^BETYR-uJ7hrkq$n%BwAi9##?6+(~d4gPoKlHurK#GwqwKu=!` z(w=Y%^IXQLa=G|P@EQNc9y}PbG$VZ5(w~R@I~^u?)LclmSt;~P+rqGW_#dvzB#zRb z5?XEYj)0$SVV><83IDsao4~&X_g{v|eOTgXln$8tQFEr;Tq3mcZx$Mn?d9@$5BSW- zU5@{+mcIu5u@ggg)JFJIq0w>d<>G%BeCG3s&(1Z!k~nJHn^C1vqkZWtp}QnyM(CA7 z=Y&?f{RW|5D*X2dy-Mg$3w?pmj|!a;x-Rq@q355$vMv?+WTCZBo-g#3!hem>>xI5a z=vNB;9-)guH$#1+=1oGs5z`^YPJ%Zt!*qywP3RNPA$_&bCu0bWnu5^hpzDm9JB9wb z&|F0uGXm9*nkR(*ywE=o`sgq6&*{+ZAZ3H>{vTa%3QygAJ0{X(}3{Uf2fggygZ zWYi1_{WhVwqQm+`Xi|FJ;|Tn9bSIjp#T+T?QU<3%yDM#uG0(F%X>*)i=`ham2HjLH z#?e`t{1=<^!Ciwqxh}$fWQvi-@pJi1fL?_kuNM4BUnw#yHlJG5&SFxoqq&spX&&p*dp<>{r~N~KM!tjigc#<)|f zLpo))`1l>>ot|E9?(%e(c|Yii)cqvGQ-{|JeX`-12&0YT=j5LN&G@{ihrh<}t)ho+ z^QyziWx3r-2z{>CVH#_jg*GH@UWyjbD-G?RhCV6lTcS5k98&Lp&n+_mAUAG3sx>ZWC$o8gSq%;qdY2LvM&Z7eoF_eYyk)O+xix=7arUA8CT*SDMhL*W{AM!7)S@o83| z+!)%ey9-({?CpeZ>m8|W7@~uE_!dGNtxI)7!9lpRW*7;Lu&$#!nPQ}Dc0+A&s5)58 zZpvkcfQ+R1K<8&7Ke&6!t{xc~5@ymu=beM>_iN-5-UL5>6n7w;=sU0KaFIKhEAD5e z`-EOW0N_y$R_+JFdH+5|?~kF~*xkH8Ms3O%+RX!+(NS9r?IvFAVrVxH;YK$i^!5M% zycpW8z!^&bA%PEoS-`>s#L#XrwA+6Ps%xKpaKTFYBTQz?$7P*ybFVO!WZnRu)gF8(%)k(mZ9E57*tD8&2 z1Uv{?3+h(kCS(pwmXVExB6b?U;9-66M;utSrdT|22V-bA?ATH(x@BV)L%R{q*E%kX zq22xhdTm;PDW(FN(UnwV|VQ`KZe|Wp{S>2T|J_sue@K#n5gsv|9l9 z7DKzW;aQp~4D}IIygE2iwD@`eVdTRMLD`F;-2gnPNPr6W7el+LIutLU+dv<+Aw9*8 zq1|Q$orWt6=|({Pe+)HX>QftdQl& z_Fxe+i3_j?k=ad!as@k04Zy6^rn{;_7L8ioENo0!Z8&(t!^T3kG%x}rL*~-89`j`j0~ML(tR$&6ITd5pD6%mKv!X~}@|r`>JUgYv zpQ@XYLXT}jT}z5C(djg0O9-}2PTT0_YkfDzV?)(8pSQIqOOak3Tudp8N>>Wn^#z=a z^()~aEny&~rQ9T5%9cWA4DA*}yRkpyh6ej}Iej=h@%mdg0}Dl4CKUC8|y*efk6S)Pwl5U(`ipHgd@ z2AD49vht!c+S?g*V`1|Kz-{3BGW>6rRyGgg;S~N>%lenQ8KJ6$o2q6hG0@D?4V4O` zFd{zC8OEwzZxPphVfcst@+<5Fe*CVMK4q%-dkh)&nrHu>jK5^h#gE@~(zj1|xp?@L zTE6`ZzVBr`ro-pS4e{=RjQsA@T}}*s;mR`I^$6n8 zUyi$6Jbn@z#(Vum{603iTs;1r6S2)&d;r`G<{{v{06)ryB4*+zU1H?hWdzCP@^km$ zU+SCJTw6o+2*i6WIl2!eX1LR$OFv%;`~O_8MHl}3Y2p0hG`i-Z7NMN;%+II77`%7i z2{RS%`E@tHFJ@c32&wtKFu%M0YkQB+)}=qKviJN-pY>sz9?ll9-(@!Rd>L5+x-xBy z>G+WWDI_w9uY;-q*Zyhi3xS2D&062H(q_GRg_R%;>NTFe+KhPmRc0&bP59t@6kpAU z->L@e*A<)}RR&2^=guc#y`<@n0S4>ISHUzTu2H&z5 z3~ddswCNMyWl9fP&pzaDw4O&u51}-@()e<}=IN4oqo;2$Z?-gLz74;xh|D!}8)Tjk zIV&Rb&xEc9GP@jE){TM8o5ij$+q8MTf%_M^((oa{GI*uU%|<;Ik-o*-nY6jp`$9-V zGMF^E(&jC`_0s08=1R}sW?t**F=$0EmaE#k|i7!JU zz0&3(^LHTA(x#sX>i)@n)}8TrvF`Wcca7A2he;n}#`~qd4@=z{CvA4{k6dZ<)u6uL zka?d0Y4ey-Z!e_38T9jS!2&tk^2k1I3*+#jJg>&DBJzCK{1xQEE%N*>|Hzd#-w$N? zX9KrLmj9!`hWt2?;VGk@UzpEN0zV)1z|Y6dV7&YrdY{YlXXbOBuA8rW`sd~ePyf=? zJ^d^5pPt?$KKpo^2KQt;d8f@UO_!(lnhQMreRHL!e`j)_Z-X92&HoX-Y5RU}pKY4A zX{kffe}=S+%@I}__3e>3&Ws-lPA`7*Kz{&n98G^I=t6Q`i~mQJghh(zy94^MfNny0 zqqygl3Ftoy=zk697h&l%DzV`cCZIP4^t%K469N5|fc|MfpNi#*&oiKN0sWSM{!lydsw)yiS`qcq_Z$KY8KZ;Wh=*I&3Y#1Jw=WUjzPr(gn4D#uJ?l7n3+NZaSdvd4a;XCpZ?q4`=Th$ zzXo(Wx{gb`Eug;_&>ffnU7Xtj`bkUEFWTc6pf1i?D4R6>mK~49C~5iz8$4NQ`siAE ziqbds0MNzKh-SWNY5LzPw`d&t#QKxdlxCbCTbjPC{^oS$(@)j^Li@JB|04VR=Rj|V zPa|X_SK9hz}J!dh_a)a%R<#)+==1m$i(6F*W zpX>d!(1*}A&+jpx@$@RpBzmRI1?GQynikiX??Pt!OXK?Rr$V=iE!r(~o2(z0WE?+q zak(b&KhyGJ-G4*^g12DxbLR18{4mhl;YTfM3%4qw{A<)FEs~_o%MH4 z_eqV@?)g_Cjb2;>U*qXlnto4TZQK?-ZC+*G;Q6mM@A33&AfH}otTsOA>8$y*r}O6T zJ>6?CtmWc*^`AUFY@YIT(fkbbV$_{J_P7R13cW^jc%IOgG_1j%Z_`fTFVpg(KL0O% z7a@KNR=|r-Fym*eq&};GK5L9B*S^=WZwTh@EkVC}Q?N#L-qxt+TLOLF8mvp-9IU!gml7S+1zT9nbo@eBJ8{%WX`s2a=>k~G2w%3D!%wL8@)GKZNA@DKwu<7yqM=&~FoUa7^;j3oU z^B*<0d;06aIQmCb8p61SeALJJCRROqr7?=?o_<{VJa5ymQ1cQl&Ueg0Pk+}C@rPI1 ze9sV~Oz9`hYES>bT;}NsbE~KS+5D}ie`M5iAM<(2{M7UR#T-4y<@pnn_H^B>_4H58 zTRc5!=v!H3*lB*?>7N;{UzGpv<|sSyq|Gj~+S9)9oX@nMF?G<3g#N4{TGdHHf6frbPG-&&#XGmnA3M(F>~JPNu`==;qhpesWEy?F@qsL)>oq|wyWJB0p{xgYd)p~ua= zpjrEI{G5H50L}XHYQb+g=#L|Qi#aiYbq7=c)hS-L%Om8VmQ4W8~yZ1nW<#0{WN?4fLD zCD`s~2z|E5$!O#Fxw0leGd?fMya~UK9@6I}jy~1Ri7P~Am49UdOKNkx%$41u!wV6A zyE#{Mh#=JKyr5p`pkA3oWDhS&EMa6^X>)Pn0wqDONnGpcmnEXT{L2#~o_|?_a`H-> z%M))<67&^`w|RP9;{Be!D)CWIuTOm2)2~c?-_utoe(mX3C+6B3q|G&n(>#4`BC?0s z#7jLtmx%VE8xog*{}JeC)Vx&me?<@J^F{yKE z^<3DuQV+MFuElJ;Z+(!Bj_cX9Z~Z_I$IDTM{hW16`xPYVqy6}zU*6*w0T+GuzC=mT z$NT4S+}mPE%f-FzDob-b(sq5Ohh?2?K8muw3VJ(6C+8Yo^u_Bg%pF0wrv~Sur&-VM zjDPx!J@0pVnE#pP-+lgPNd9`(x+KWwOji{0IVUJEXAC--11L@-(N1jzG@NKnCZ9nDt#5$k`plIX95=yg<(L137yF zIlG>*oJXSpTTpV%%eh+QYy*EgT9k7fuQdL-D~%l;j*#TSoaMbgr*VS7XUx3P=4JBy z;#}l2ihStTS#_9c*ps?^CXj~Db$L;Tq%RcN)|yXw*)9!qc$vtia$YWb6Q<>JD|cZY z4EFo$0y(cTXV^%5p5@9-1c^t0i{z0J^fttgwvz> zh4h;crx)o+KbPkh>Vfl1{_J>uc{a{3Z$|slA3Xaxni*p-T6ghWbS{~=IA6R_Nzg5U z9Xn0Nn8rCP@X>yDFxMna#;0xfqdW~ObB*V(49*SC_3L^1K6hty9LrVw{!C=dvQ?i z8k>^kUJ{i1vfvr<%Y*aVOD%E(<6q|2uh+qOBl(AL4VZ{wCDxD^l)~z}AK9^RoiGakh`c zd}t4N(GF}wKHo+@q;Cg(G8WyAelFVw>fsjXq^}k+wzFDqn*PqbQ4KlHV5kDA}(~3VrlGpMkn{P7Ct8&gRMfzcUyY zq<149$A2!53+jR6V&MyQTs&xX_FNbjzeU|S7T6z-Nk?O02R6Ig(VN+~pZQogreQ2Z zXV=FB>(AqZ=b61P+58sJC!;RplYTDu zZR&x2`!{38@Le6!YoX9H!5{V3Dzc577Y=s+rwmU&jB^{20h1A~*9d*I&}E_ZDb8Dj z))Tk)3eDYxF@G!c2}17>T6^2~h33;Hyz3+MVxgNdtnbM}pDgq#LU##$s?b*m-6HgW z(5DG~v(TpteV5RB;`z5i>ru8~u3f(F+7V5Yz61r9B z*9zSxbXDl2&|^Zk3;hA1Q$pV_bcfL27FtKeq|kb1IOihD&?Rw>6&f!A;Ytdvr?hK? z)+ZZTq0f^zn}j}J=-Y+H#tzp#LgPPs-DCgq9W%D?BN%k|N*os>>SNFJ-81I14668# ziz&Ze%dquz^iV)E)XA`~ch#QB`)Qv-o-J6F(r!2#$h85lv|qWBUKH3moM|BZG1yFw zDNNV?Jdi!{%aZ5uW;ODB0`zvAr8Cc;2(32rKZHJ3)*JIKVVvV+-HvD|OZ&Xii@iPM zI^3O$a{b`e)x@mRUdFT`PTiTZ$Jcz$3f|8@$K=Vx#S@A_B|*0a@9ih?X1ZQ{Pi%{) zJMc!f|DbC%y=JBknQRON)hmv8{CQbWl zYeks$8jswW##2c5Jd*E>!F3r}lLvL^n)^D!!aR%{LzFz#^;b)%i_fPWE z_!1z}dC9Ew`oF=v%+qCam8YxbwVtk-UQgd>hCIF5Z1(i)%`KjOgL%KFx0nxl8gFNK z`WADar?;AMPrunb>FH7POHXezzxDKO=6Q2meQ!6XdioC2>FL|eHJ-lH^m+Q7<_1r{ z%iQSccbh-)^quA-o_>${jHmB1_kk{=|L`rhIv#jvzrD;n1^S)BzuZiKexJ~5%@09q zf4;&z0s2G2UuPZ%t$q7S^B8E>W*k4)uO>j>Z_8!hCjCjIZNXb{sk6=a7eQ~wHxaZ& z-x2z~flYfKxO(wjvgdjFe+7N=!@>E$M}qU%kNLG(+I%Yb2H`WodFPrfAI z`>?n)YICPA_san`=0m|dlaHA9`Z!-PpY`D}f_p8mh)Tb|xye(dSrnCJWcHf0ug+9dccyvj2tahj*+ zC89q6{KTt0|FA^X(=SMrJl&kQ!_$ircY6A$#CA^~llZ)+Pe^>h(P9}cq>Gs6jxp?myeRb6QrP%pj2>n-L=XD&tU+p}M-za+bFEJOqQ0Tkm z9j)VqzDM5C()s!W=3fy<$I)NQJ6hDwIDXF~folRkmdlIds0s8-kPn{QCeB@E#ydc7 zHyw#+ov_@t1lzqk;qu{oKG9jhs>EXC#S0j!iBs`cvclw9-nR8xV~+C-^1=^5a(F`t_9x-xrPaG zagExqBY+zmUoaCjeA&_1G%_|<=ExLJ?vT~L7#yyX7ozqtAOh) z-GaXb{C0`V=a^y0oVb*7o+WEqmHF&IW<7^ta_(~@ncD)Hlfn69Dv-Y;_{M6vd_$r6 zbOqlFuf)@Mz4$J{m7ad7d}qpA%ACdT1(1{PQ#}fqj~AJHMCQ|lUL`W8gkCLs%=3l5 zAUG39GdsEXZq-Siz9jfg_T{F>^Dhh5;g<*B0!hV5_eZdX>EUloNWj`A_gS3Vngt^P12ZS&zL*=nEU(>2fkpAaCZ+i~8ZtV+`^6 z6l9=HrUc05W*W-*+Mw+=1ZN=m;F(Jy_^!V%XqN$>QyM27y(|#>W{D~*$sPkDNSxzE#E*xO%1Wb+=1Pp8zL#{@a3Y zncoaexcGO99-bB%ZWfstGLE0iX9Bc4H=qopcO(8{-&1vt{vhS~L%8gJY~dRV0{W5|1y_83FHOz%;x0H~*<*|sFW={+pHc$X5eb+}FKsdNdJJv{%y3vjZd zqucZijpQtfI{c|hMoYQ{YdyqZhr?nUBv|eCU!-%xmh>t}31m;S&_L39jvGa0)$Ara6G)KA0M?SCFeF0z5iJr+;$rXkRoUy`c-% zySQm~VBd6tu3Y-LbzHhX7q0kuc3isRRc;tJ#*laVX^S!BohsTbFfrVZ#~AXxF@}71 zrJBuE2kE(+_KEnt);D%FJKV@u7y5_~bhscsUFeI4@cmiv&kEN~gIlR>D3r^^k%MK> zEy(*p&K>~aoHY)LhS(U$n}2}N4&)yJXvb2tx`{}BTztYmy0}3Hrxvl?dXy%x*ZI^I~i!&1sHk6C`4Q{c46^|LOP^LH*E*_{8F^2q1uqwC6jWOht-RLbfSRp#> zkGw!*%VhywT7#>*wY7b@zq+v-Tj4O+(W$#yTY*<_z{gsC88OXExpIZWt&;5;exQ)wSg8RnRhSBcI#8g<7M@D@ieJL2gj`}) z!CVL%=hh8Difjig3Siqch5+4;Hin3IVTxo@AnSms?P})%*>P@&I`M!}s%CN{wQ3e% z7#l{0LbP_>jRE*Vk+MW^v1dTO2v}Ni#oh`kT`p9xg)3lD8`QXaW?6Ew1UYz&i9a!h zJQ_1U+zS{o>}EuDwp$2Re14=xyh`QA81l3Auoy#r-|KueqcMiOfG-c{O0(Y)9BbLS;+ts0D$XPze@t04p(^8yXtPAJ|nzu5>{2+l2m#46E4^fY6`_ zV822UQ05Zys+LFP2(OYIy3s?q^Mt7_8TA;qfGIKtk++ac0NSB)^b@F@*jEyQRe*%Z z{G7rd3ED(UfGnxA`MeQ-AWV!QuO?(U+o#f{@S}~!0u9vJT`BYvOl;bKP{30=MmS_U zyKN3IE6!A4_{13UwPiUz6scr$F@`(_AG=J9A)mea#*uO_%p>Pmm@ZLE=MsxY9%IPM zRx!X&jv$&Fu+53_jt3_&KGUJi8N#VYLqbe=JogoDBd|H+#~AX0avEdE3rZ^{uNXr< z#*lABPLDC<@!p{VrQ>aj_83E+6Oh8i#~AW`ovG})!jP_N@Q&4aT;0SN@|htjCcU|+ zib-*Y3dN-OB7IwDFOIVD>b$N4acYAXhS>_^wpx*7t>}3PJJk?=xo&)eNHu1=&m@Ve;M%L?G;gU}=+gqsQ zBc@7JY>?WWwWwZ${JD;1{!sAr{Jo$J1;}zojuCgKla`Q&d#d38{TI!fgy%w zfFJ=uCLurqB$@k6GF%cehZq$Im*B1SFgfSUC7H|NTtb3G4n&k;NQ%@})PChsZT0Qv z<+WA(RqHzeO2o@6w$_h+wK`g@qmydXyf)R8`Tqa4_TJAvb3tq-U*ESp&u`8=Yp=cb zZSCu`_g-raW{cYjnN+sFJ3W-g5;H%P?jvV!mUN^7TUu$}ePEkQaWT&9q^y&I1K-EMMXcBg@A&tjR4t8{%>K$>9q-D?B0w=X-*P+O;UtVE^qoYr7q7W33)%hzA<5HeYnI;5JlKLkA167Q{I9YHVpm zw_Lq?4G)_7(%HfqTm#Le`_|wm8v~i_PPQmLzH4mWR-t?HxOD)Hak)v9t&v9S~nQ2p6Gr8z%2jSlogk?H%d?h?W=1y%d-Pz49NJx?CBny+R2BC|Sx^iGaw|-Iu`L2F zXzAH)+XqSGYF`2h0%6eGBuX7&>~1RqON`tb2a)WJ1DVQaZ_`-0Ki+&tHm`fNC?m4l zkI};f&+({k}2#q6K?blZnvfl{^K@k zU(u&AFf|D*WK&&`+X4kPK*6Z~zCuxmG|5JA*a$(6nSvvS_JX}tg_*oV7k0X226J6K z*#cr=Yh-|a=Oa=cj2;6r%7^UrWQ&;Ca5FdKXo4IVoFw|}n0`4TaDjU(T!mg_(xu3F z2eY?NE!aRITU>XGW=cdzgj=WziOJ?Ew|xlB*VYN@lr5Re&Gic#l}Vg>9X}+8Qbi`G zxv+tE=7l3Ds!Kw$P6w+{9IV4!h6gV;om??=aLD6jNi+yxX8N?@utJOA7fP%d?e2G6bcq@6-8+sb-&{d*HiKqoX)qu{ z4zjy!Zg$EciDikNB-wV+-Crv77W=m$YUOI7Yp0HAY#Jf+27(sEx`m+8?LL(9UmGFw zun8PO+2vp-Ew5C%Kiie}0qk`qaE~;YzHwh$C7Nt5(~bjjU4mR5vSY6cWBZh5Th4Yi z>DdmNv>e5->k*JB80SM1V|xSO@a=(+!!NiNk|5>$1paa=FNpuaQTo!8WdCrGTHv=1j-&EK*4lC(cXu{H4Y)d=1d#E!+I!Kp*wfP`b~@XC*E0KB!7@V)BCvRxOcad|!8~`PU*Y@=N$BtiRAyUGlwvwZ z2Iz3*($|Cw(O3y)BmQvw$y9jS=E)qR8y0*yGaf1gV@D={LFQ1K~0Cat>D`dS!#P!n1e`)#!_$1)85S>?BxRD1znUygrnP7@!xXc0mNi z*4+@w@1{-}U%?Z6YuM{ihz#tY+kZi@qGY9DhdwkP3`g3&1eW&Imnov@}nBLrWHa3NdiVlS3ie$c6G8reQs`$^Rf7K%G^JzPC80u&4L zQ;*9j+D705d5kWu=ul@IOX$K!vJc3V=~9L@u4TiK$Ry@^u0v3H(oQm!xeG!b*ENMm zvJFsrjTy)epxc2P$8&)riqu`@VC48_(%4Z&w!|`}!ND;lYnPGehY++EuE@GdM&lhy zG^ng&chW8|t7`UH8J(ni@4)!V#;!vGx^kAQN|Z=}855Tpk?*#&_QYnz~>-G4v9LFu9jkOJV($#x>}1>DP64}PrO(cNLNeQ5FX_Tq^qSg7Rw<-g=3{~ ziVdWz`Jpmhy1D_q)M7Ctw7UqnA7o1qqUG9-76NG80}v5$l#K2e;0o=%B}*nxjXh0gd5S;txFdRVZBzAAG0U6@ARR${$kx((4RHKRg@#TTWgW|@gbG}v$OXqMTc z11t;du!BRMm<6_I%rL%U8(Ch9FI9N3xb#3K2k{}l!^h;xi`-xe^=OeKm&pW^qQf?^ zpo|5~$>YkI$J>%34qO%~+~M)AsNewhXgoS7YRKRwU%Q10wAL=FiZYl!60KUq+>JH6 z$7;2rB|zMlOCP8xK!@>-7$4%iHV4@TWVz7N3MpUx*hf#pz6PfLMoJ3z$;KRTeoqug z@)KhLusAE$-6UXzn~KKHi|(HQty@w^8oL$tUM#S>zl0cw)ol@A^I;Ym^qMr#qMf%`S0whhY=boH9IGvFrudxOy}FsC}HXSvNjAvA--!Wi;DhpOHI@2#!vj z?&1!mSg&a>CTj_43v_Prh^Jd4{!%d0hh-@1E*+q(@SK^c3CVmTD;kgmM*Cr&kDnD$ztnn zjln-;g#US8i{0|S$hNGEFxZSl*PFI2E1Q<`ZDre~z@GWq^s%FQ`wTSR6Dw>RnwzDz z-6B=)Tw51+tu4syR}0GCsx)CstxZa{xCJd+wF=!PTg&)v$lr$f-?leqKa(wH(=wDZ zy&xmnB&GC{RU0juw|-0tQn#FChnaP+>ovy|$0!34Kl42z?BnQA0*P-P-j6n$QT)thbl9iPzG7XLCoss#{Yg|I{5s2*f;JEA*v%z5@3D zJkLc%%rhrX@pnqao&PDGKg3jBvdH{t%E<>zTg?>lf)E$K6OpN<^-9Pjr8{%EG=!NC3Ra8phEg_gb= zd|sO{qd&IpQ{cX)){H)mH{)YH95~mEzQ+2~%mCHIk}veHgZq+KnbBWZH%+2^=43Pa zr(g*ESHexxcDGvpdbks(B0jw7Pv!KVo^M8_3~W`+m4zd!89>Lsy_bO&(BP zDOHEk{XV#9%A4(q?*9gNKjNc_6uLhNH%-)UwC>NsO>-7h%TNEO;I0Gz9<%-j;r`Mh zGs<>J|1ZOR)&*wtdc5iW2HrG7-)a3hR(=@u$9}~6{sG)i!_A&Z_XOND*Y47%Ns(%_ zXBWO2eE9q;;14|GKLzg1zhy?r7rGb0P4hxD4Mg{=;HH^+7vCzlX^ySc`ZvS<*fKM^ z)4DgpeK*{7*1ZXCn%Q;yrpXetY0tk5+@ttA)zS~Z4f6`4Z@2C{;ij44Tdey5xTz}s zaqFf@lg|Y0`Mq${lo3tkF#jKd`w8%Ov8DeQ+*BW(weHWrJ${-QeZ;yCz)cg-XcH3t zi*WBc$&9YI@qG>M&mp~&;7@*k1MWXsVn)-}|NC%L)pN>*{|VfupgnD{?q9$?QVkQS zmi`#rG%d<;lKHJhJE0l-4(om;+_%j$qvu%q3*b&IK>XId67FYDH=`f4^lRb14{n!U zC)^JsznlY^-_3A$BEN68^v0`+#d#H$miTDq9#XwKv(hsScmzj2a&QtNpk#iWk2ZmQ+Cl2|t{lUS8gv;N?V-&-~Q@f#O4;S?amM13ty2tCcbS+!- zIFCnjHW5)XIE9uQwf^WGX=6LmFYl)9JiAR>@sNn^HxrLgR0Wz0aenonEtYmeo-R>)TE@8i+)SoqyZEkdf3BeXLPqO9I zn+%s-Z~>w-cFzqP$W|Zr2a#VMgyL91zILHER!?WOQe?r(IC=D{kqDpVz?-?w+e%<= zcoHYUOz&u7&whq(euYet`npZ^>Vlx?zI z7~dTH&H(m^ogso$mgK!VcAfx)_p^2DRSa>)pC8(=1_nB)v{C>aX)CmKE{FdO;34>F;3IhJ4z>L+SX1~Wj?)Mxz%?AUMgC84^G}*z;hRX?-Y10TI>!F@px=E`!tr0H`SXCce-ZS%CxW{8 ztl3~gbi%;mfQSEwxy!?&et)Oa91znk^y|u{8qx%X{R|Y(d_Z3CprLYU;DZ6N88Mi zS;u_R^C7RemvM&h>Q#vqHQ1{Gj5U&&h*VykEv6t>3I3b}_nP=%jbvjXd~xC=MFU=u zIM2hEB%(3=(!@rOzbp~8lhuhnk6)9x-NOw5KN|xcHYcJ!*pi6GsMf^AHb{%Cn7VyZ%N$k@xPOJ zw};=FcpPvC>Jg@=#dPOu1U^qpcWxE<)rlh*!@C7OKXDlF?E=>&4gtPf;H8Oiz#{@L zOMDOT?+biEVhr%(0xwT|6Y$3czA*7TV3v3kKi5vi05d;)*bccT{8`X1hw<63Voyu^ zY)0DQv%o)UvVlp`Zp%&9?{{G%W)!he^FBsE(_nsFXS+uiiV&$SIw!ZM# zdvHGV4E95hODmr+F18+@)*)MhBVIVJ~*GGNc5l(U+@GAhulh7(?by_;Auv<4VMdZiT^^?=`OJKlX3;?@ zoH6yIk+4%mhLaR6UDFTy7SI2*wC z2k@T-FtR555AVRH_>;ijW6N?%bp#XjxW&IUz<(})Lz=Va&m4B=Ecma_g0Dwsb@>V7 z-*53W8#@8GY*+9Jbe1wqFY-A{;3WcIDzJWQuNRmn0LI)R@VNr_3#>E8Jp#X4@DB@I zC-6rFUMlc20xuKz9|gWZ;9m&5T;NmD;L3KTa*@Cn3H}O!FBbR~fpwP334E#G?-%$o zfj=bhDuMr6;MD?uOJJTl8}q*et`~UWDNMIP;AH|g3cOa}CV_7dn4^&~+Xd#Sv@v@H zMrXnEK7q0D!Se}$F%aT8DDYZ=#{^y{@V^SIJ?T^&B9!eH_;P`-5}I`aZxDEkz?%f- zKi9^(foHvc@jA3gq46Z1W)d4DGqsJ>!kO`(p9R14RHd&6yY>Js+oYZd;3EM{ZNahmhty0nLvsyaVlh<07VYx=k&L^DV&76z8Wce%U!#FJb&D`>)d|>^|Tbc4QmQ z#e_zq-+{rgjISj=_Xw=`Ukbch9E*^ZvZBp91=cn|)Z-RE8~h09U7jZbIHYeoBcj4yYnY{q;*U~QA14)9N6$S9kF z65;<$e0Ux-uIy`2H-yh$pD_0VF58fAz;NUEzguAS_`bl({~8QKj{Y`*mH!_I@CPtF zm8}ug?+C2%Y{F1gw!2zg0xLdoKJjl+2mZVKpAS6q`EwL4Cp1d(ZGqF`__qK*SjTt< z1ix2c0nI5G3d`mWalBe!#or=uSsnQA(j~&d1E66ZmVi&655k_ijB&0ZsXUIp5qQ$y zzTTL7Elh&Hv+xrb|1QE1T(&zG>ja)YwlK`&{Dc{S=1WXM{#cS}r90CE|Fkz>2?HU=Z8K#q(a^8IOA${s$Jn8SSwZUAJrreqUfT z9Q!!>j{whjKK7}3=3j+I$M#wjscf_@T`F+9giH#&PT($qb=bu)y6VbfGfV=Y&_G>0mEN;I9bGRkSffV13y~~s4jtjEcmAdenH^+M$(*A#dsbOxJlse3fwO63N(?j=@a-qfw`i?`b1zt`aJFc zydB(%>RArc?p&Ah=>+T^nvXuoI(SC#N)NvZMxgZ}jn2{xd!CqF-i$c8F2Xu^l2oJk zxp>9^Z@`bwa{LJ2BzdSapF$qC1AY{Sl50qSr_K6IEjoklG4+@XNLmwO)4Z1p1R(`l{@_MEQ@OiXX^ zvvjAq8mBAzFfBAe^FE|i7pws?zzri^@|`e0taxVR;2VL{Ui$L;P9Y!eLHXU|coOpJ zqcN|9J>qOIDSV~$NX z(g9OpFe{x(Z7cP`oW&pncASJHZsSAl%nC=Qt{#FGZkoGpgxDGRy9!Eej$5463XmSp z0N`X%omIQobaQOFc?O$hGYT(@t;vD?X4ihKDO(rIax|g3@^@W617_Jh8}gdnsdJS)o*jP$6Xu zye(;^lriwGsG*cG&_Pinabn=LJ{d~|T5A){B9bx&vDRdTlrgZ{ZY!jWfz^hKTp!Qm zGW)0j(PfGyS_x(mjSMB2!DT0e3z1)yUWhYK@q zp}AhvBhe}PH)uRs!lk;Qc!6llz!En#udAs*^@qko>+e)x3^Ha0!SpOaF!xajhE)|x zU&##TX-S^SM``C;v%>`@R7-#?mFrQxQe1|J&SgYFJjAy|=>X>-nN*2%8RbG$lEsv! zldA-=*@Um1TSZZ*BrCa*Wh%0RJ$y3hSUgmb*@PVbdrwe{C&H0FAY8p`|gj-Yr!G z(R>KJ-=1~J+qEdLrCiQqHB&SfPEA%~LO?QW4Iad%n{jSq>(4GtRQX$2rIDupfORlc zE~5Fa4rK?Ls(`VAN7W3$zB%OQv1B3+o0f&fX4T~>`s?)SPU;5xNqQsS`u>y z%s63Huj{*QHrxrb_qnOJtKa&$LuSneA`G%9w+Fbf;yt50%KA+=U2JkU?TQK~`?K`L z8eKCJn{GyHaGUX2>WM<_Xd7``@!8mPGo}TYM8jTGHZl%~e#D(UJ72((HFST$&Kl4- zQQ)~blvUG^wPPsHz@gq-uo0VqrO|7_+HXS}G3~M96TZSkHrlOhB}z_my+~3&Mi_)% zSe#lfXCzzDYA}wqJ1vB6+tRBdL^~p6X$#bZD__Dc3CyMN>0tN4z_G;}gyfwT7M^!l z3Qdi<3M-HDos>&~6}07Lo0op5<#1l0jDBn7!yySwHZn2c_mQ3T7mRDh^iIX zhzVq~)+^AynMU;rY`_LI0y`K0@3R?HEYO}0nxd-=&j>+VP5)Cl5mY*23Ryv)W^{ct zHQyX|59p*Um$*=F-rBDk@+bGkK*D=s+;R!>7gbeds>t8;R1w9LL}fs1 zXQGJI>bD0Ks?XMGP(X6fc2>v18@)+nJ4EnmTA@83R=MLKtav)E0Hyp3;_0%BG21a= zIRRDC_>yDFv+c>Y7qX85NUK8P2PdO^+D0FnZr0%-Hr*VXZuZl)SPI61z}l1by~yIz zY9&Ue2c6@rPtJ9`7WCS{h)0vy3g!^SaRWcGf($DjVq8onHC|-F)D`$K1F(YXe7W!o zeA8XHBj`O_IAGdnoryaRQ>`*sN@}?Ze4E^DVYYKU`JA4uf}Y2)SY2vy)NB@``LA!f znY)hve_9sZk=?xw3YH;XhX23b)P$Otz`30omY6d;6OfxNLi%n=!bvDn$G$#G@gjO2 zew68?ZlCaRbP%N$+0T&9$*Pjx(LoGa;vknEt#MVU?e1mmUN}uInoluW0$w?L-&1K%%r-OgK%`4vkpi=R|U# zY$Ff#JhKl_Cim;8#5JhcCoRp(LC^D>Q1<1}G&&Aj2|a)xMJr|>xl7>F1APK#Sgvqf zIm?e{)NjBsj}LVwU#+0fIPE6XncU;?=LWh0ULEMwJRe$1jX4bdm!X}L`a*}1hcfgn z9Gyi^0_piI$M120R386+*j4y{*IU_qzz-pw4E!Ged>!6SPXBiRm+|BCF8s;_@H~cJ znE;;W@GBF*Qilvkm+iVAsx{5O}tB{>q@8p9Kvc`gB5fN7Uw*cnvhv z0pi*g+x%4^58E?CkK*Un0%L$T;Kye8|wtvv~lCQx)qzQ+q$x@X*HCa z{O{)WHbjo+zOY6T>nn)$6~y`q@NteEt{a;>u%ic61hKw?Un@@->wk^)zsCAsx#H00 zar=(-zg{Gqi1okfDm2#rdb;fy(YIx+|JAd^e{aY7UzI1Z{?}OlYpnk@*8j@UfO|g| z*r77k|Ei3D-IZAXD_Bvs?c(bJ{C~Qgidg@vR_aT!=@ILHUD>g!v7x!GxxJ~ixxK@K zZ5^gGh`S<#*^J-W#O<=Y2r+ilhYws$ZT{(iJ>$Wix@m!Nvy6dTR=$M55(AXw;ZhoJ z=0Qm(?hP_@D?&GfX?wV%-sd0$Y;I=G687qR%x%;1)ey$x;SQLe9m*FyPjG#6JuWRu z?%Ki2rpC6`_746B2bFXhDmQR9jz@bO)Vf-cGHTpl$^2!vHsV1KlQleyk^zU z1`j9=Jx-yfS!16I4L`F+-qTggp0J$Hi58t%v)(V*RhY&n~j(kg1|HSFHb4 zbk{iHHOSYcDInljMCI2ci1ok5`d?%HuaNG5)J2n(Yk__RD(jUC%7Rz%HwbA!;YxtF znzW8zdHfE{7m>s<95S4jww=&f6lqXF>xo(c*yv;ZuM~Rf^YT3nvHsWoQYH%(6Y1Ve zHn%3${~GIm#aa#Gwz2+KO8%*6UabEW(xWe~c--mda05xK|8-8jT&J=%*8l2MfI77c zg(?x0nr%R*xgpm7dRZ$}c7y-jfl}94|7%xahw5O){yg@v zo2iqTDw)xcv5kWE1?i}vd|wgTL~M%!lI2&iK5lSPYJs5#M(lsp|xwM1T7fQ#X%H8;*LE;&a?g9sX{g_NC>ejvP0%mdJjN> zVM_IvIwhz&HHwGoNs$EY^a#zXCD;rUown4I;dGdasy(X9wVGM2?$yYk1FR4TX!*8jS-kj)p?{D;g{xz8J&lqvr$dM^pEbI1B$WBsrFToH?cRjPa1*1sb(&m)8U zV6LktYd<3}CD{eB=yA7;WbMt}`K(j(8tZ?J%&@2mKn`P6Hw&G%ZTQ@PPFrf7RUNc> z)<7F|gBDsW0ga3syy8{XuXYXVb-h!(%2h&zPoP|t6-htUt%?XE z-KtbnRuL@L|7vyILaCgxM)lu*0ps~40mk#z1uipAVf=I+Q#0U!cZj3Js0ULtvPY>w9-@To zxYY0sWC>hqK1>HUm857JO1qjGH&X$yEYzE=9^PgupKkApmUdWm$Omp$YNyM%e%y8# zMA6zDL}3>+EwUHd7{uXqBD+YKAH)*y_&UEJjrG6!{R6AbHP-)XKlx()uTTyf>84zF z4Zp{5iF;kn0eIsZFE`{5v{ip4HCuujJ6OE)&5g}X+}H$Zg%-?M|0~1XxO4(!E22b(xF%1~b`Z3-&Bn z5L0Mx&Z4mQYOO^UA|iY2?m`EG zJ@(i=mjCJV!XPc+39oSYEy&AS+QcZUV_&lue`-;=sB}cyssd4tw8fLQHcACj6vfx5 zT&!@Ut&s=Km$i0e0#aBR3u?bDB8}yLiWKeP_bXob_QZ#0EdRp;K)FCDybuoY;~X5q zKEd&PaaUd-q-;U|P**0KKTd@UmC<^+1;py%RBx6ES!o5F@ijFLboKN>Tp|)wu=0h1 z&@;YtZ#KQ7P=cg|AoU8X#uUnE=!nUpgjkN67zhLn`G*QGy_FQp|HSe?ylW_p%U%eK z<$shN+NU6eG?Xdz>n0(jopAdS7n$+Kg<9AZAIk5>eaoEf)x4DeW5jalJnGfkx(Zq8 zd#V1R+byrPcSk;JXs1N;$*)*FX7Z+Fq>PR=3 z-31il0%t{VN3BOA7AtV0$>D}MWqMFFriv-lR!Jl0)k0&vz4K3*9*stBa3Gdg{zqyG zo7pdVr(f@}axNj;dos9GBP^#fO!9o~T(+iI{%7j8oXSOMC!4!oyHg`9rg9m|3h)v( zYE$kr04MST!0{YLaD`O=?U1`+*T#ncyLMwF1%eJMca#Q6K?3csB;++CG#uKk6(4j{ z^km+HK-1yNc6GKaO#yjnW>4iydUJ1&PZ$R`vHVXVTTIob8lrDA`3}Q5*?|fo+{J**y(5azu|7&Tl=maQe;3OdQMq+&+}&;VdO->`^mTJCtl{ zZ4P9VWc{XV4C{B(t{^0bJNn`?Q$xNn@MMkA6KBEd8Ob+chEzWsK&#&kB}k#;drx!3 z;Y>Rry<_I%}M{fEB=L*>vy`PMQLoT&bKOq>_jj*{?E=20LQ7+Zp*Pkui;ciLW zFQZ%v-$4OZcRoPe-_?U1N<@onQ$ds@*3}P%?+$Cl-rUvjpfDT?m?3^&w6q3=Va>== zC`>@A%O;?+ucQ#m|HSe?vHXvW9I^b5T}!%w^Tup{m#!V;4jaarCVu-gH0D#m2!fGI z2Ah0}8gksQrC(fm?m3Ct8w3tMvvhfa_-*u7?xcxwCk?F?(K|VoAbJXpU`}v?BUoDn zIkoa9&6GcBY<}rcRb(}m|B2;)uuGWAWD99KZAbIxV6-tpFuY{oU!I zJeD{4p|o^}n`L^(;=9n$(n?{TolsKKH-rgZKLND&&;!4U?F{WhvHTBM!%MHiFLeDj ztktnjB46^jf9v@O13M0%4e{*|zPI#+ofRGtgY!K>MD1FoOdwl<*EOD0$j;DXt*#;H zG1z~5&Yn4=ZLHOWo_KUoK|EJ_80-Rt3QY|iEZABQWBrHKH`cA(eoUqNv#|H$zp|8M~R%)&d6ehOo-qxnk@r% znr1-fBmOetIW{>td6mGgG)DoGXcRv;kBk9s#gET&{2ZJCd?VVn39kR7k82Y?ifE1L zNv9iA>7juf_M$SYjkB1D-7gwTY?$4&qc<^#4Kk88(h6ZiBTN=nm|n!r6MdUzk7$~0 zLu^tpNb@vvim{Ez*hXZb9TzsB5`%41KZp8oFU`7jkU}dqrEUE@OGaE+Xy4e}ft}#MU)pgNt8-$EA>I&?;!Sjq1>@x zlfjn4C5R7e@6^>pL)^1~1N#fK!MO_2A6LJnoD_cPIlBGl#AmSS($_V3oRQ8d!vzl< z_MNEMPpEaP*hb{cM#XUC&~?IWe(?qXn@b52+hW9)TOT;Sz<-=Qmw46$b=e}-%F|DN z4g;(67D##0!MOuC_FpCBX{SiP$}x-+mE|F)V!4&#uqtbd164W4aais;3hmC2R%LBL zi{#rKsfyZS1dv>Lw4Cy4AwZ4W47Lz*!pq|{r4KxO@j#CwSVylV6kb$%Xem*2ps~^E z=(U(Q1eW{+9$Hc;CeLH*qJYC+A4E%wByf=hw+B3)-qw1izX;z8F3zR#WLbkFQd9eZY(1n%ZOtI7t4rq zmBu+p?)N}gB-M>?*AQmotNLnaVdLOy=yi_i=tM zI{CJ__&RiPxq-yK=O~9R)~>?99LI>mWl^mT1G#e?b1Wmy4;{VShbE=xClNppTxlg5Zb!s2;F2GB~(nIySn7DQA zr#I3Cq02qondi;;ioDF0kjoPH5RoNp2u7L%>{T|Pv5yE7F`=3O$qFvGQ&JexCYqRC zivnB9K{chtYNmKriMw?>`_i})6=W?vkbxFQhqHI*MHU=dEn^vRyMmvkg~c-B$u%ma zs{cc!a*By%#GS}G)&%IE($6WM4rz4=RI9{1yLY>M4a^|lDaryW#6ESYA$Xi}G&boX zq+Y&7+dJ$iVVBmp{D`%R&^9)E!B5aaX5V0#e^!1Xy%5Gog%%+JGzYswnL)PYEt1EUS-!s zq@5>v5NXH3;hb&jtbwscY(^BsXyeUltr0_rmGG_@ETsR(GU9!|u`*q^U!^@FmJ#p9 zEJ1N3v}SugofL`Y*hVDhSjU!FMqJuRY$MVd;-qZ!jFvdLvp0h&PEIh8NfEj4oHx1| z%ZP{4kFkt6q~jw&_^8QLNWxQM(<&ASMd69IngyV=%S*ir8k=iQz!UeRZi;01dG!hX zx)EP5h35J~Z)(s>R@uv=z^Td^kfF)zP6Ut@()Mn9#A+mDjBbGi(m=iyd9=$c;B~`A zf{7O*Gl&QQPuB4=O%*$gg#%Qjn51iPwKY`_L|dpj6_+Or|3SfD*0G)0#Y&j>+V zTMAP-5mY*23RyuP9x{Z@VfTQp02;ZV1uJOT1CqmqV#Eq?P%}`(ZQlNp6~6PFC`N?< zrELwcJnm$eRbNhf2cjG*_HqLY$p9JKh?EWASVlaS5sziWV;ONd0TYSnz8+j5hvd6X zgw7f|&d028x(jy%y=M!~gL!yHPuOD8Z9|!mWY89L1s>YFtq^yvCvVR>LEly{ipDjK zD4PHJGUCNlcWE$uCBr=5@rb*b6)LTce9+}7KFQmJr}7x4;F!t$?$Y0ub#xOPxke^G;x z_NWcUHVQON-zXSxFnjx~*)aWblqoDj`n?q{UUp#lv`z9L!^kdiT3)kd2;IcBV4ESA zMYd8J6_kTgz|=;noXkOCsIYXW18{#u5V;;KQhAW;TT8AdBe~LIQkAL66?X&~r6yCL z1}q97O|Y^U0DB{_7=iT;Jf7{^=$bX4^cw19PUm)`_svbDZgY~6VpHB9|{j2M5{2qPj4>tIR@$dF7 z^6saT_f2t6rT@Jt{*O*^{|Ve@&QpFd=O3Hm|EVeN&rNav^%VF2ggXcRle4FxDPOT4}5FUkLZ- zK!2gN}-vD%{c~7_W_rrZ>;J+X4`vUhvaDQQbtr@cPkHGyogkNLb{|)YU1oY9xp4GUdY_8q< z`kOajef{+}r?za^luT`S{pL;A1;}fY*Is|)n^K#vyXN`;`pva+s;3-oc60M$-gX05ORlv^vh9T3i#DC|9(9rB)W)&UlDcq za(ghlbDP&Lm-_~XoU|%;eH|K$I4|HEn+WEO8LBcC?cVsHj!gvfi-n)M*d=hA@@~_Y z>r31_a=Y@yl$Csz01$ijoAUG-g7qWy;L#U0<+(Qw9qP2(Ecn<&@S0fs6PuyMoxH#6 zn}lp+0XHZ}xZK%8hv{S6!KnnU+%>dt%LZo`5c7nLKkg1w6AG}R3I$E=UE?A)RZrvJ81F(%$SQ9BgTChNIk)P5|LuU%T z({QULsnzLjd{V{Fe-a0vCZsQ$U+}gCr{Ll@{_57%ElUrjdxr+=G8c2FX4!&03l_vw zo)d)YlY*Vt7S}JaSo~8tL19dsI|dDi}PR4GpLkoHqA&IpfiaYzHzpa^pS72ECx9SE?ZEiXDeQU3WuEhnzitE*Agv zBWNuC>8_B?BAgeCe+DwCvG^yxPbpLnorf4q(2HcNfJm{GA}AcN5&*QoA;O;r75LRp=c&b)5 z#utl!^41cK5y#@6kOL6qI23Ay)G5xmn%a2pQH4%b1XV=E+hk9@RmBv-T>-g9#t$=! zIfxk{8<$uUZ{sy2Musazb2n1t`sP-^Iu`$o#Xs#SIPS$-h2)pBiYA{e4za(O=%|C}Nms2^&viC|vrkHtUTO_Nys z69XwXFak!z;-7(dDw&}isuViKa57!!6kSV@R+}3oQSm*Xi(yS+Y-P`zDSO`DRUv3E z&ABUuYj;{!*HjQiYjY5VmDz3)neE0P4&Nv4O1}P@L;{zV?Nf!@@^taeHO;Smm=~$> zG@Cl$S3r*7B=J`)@3q$&%0%!dxA)yucP03>|9MxR_w#ut|E4YDB7(7X`1^Yg)f`M7 zGv!y9gUO?mHyBAC8`*mF{?qTPeaGLFswU3bmpnRY_9c%^(&rhoNWh7|fA%Q}ykzh9 z4u6JeuY4NOz*^i_@7_~`q%JLO8%Zu4F?+{q?j0@OLU{d>;pCBVUT5EXXyIgO;rJcM z&fceMt!ptv7T_9x5KI|K9vLyi$-^V~W3Zzm*<;8k0`-Cm2S$*k=SX`w5}bI!7|F_t zy@#rMDUPw-Ot$c*{P>69kOZo?*K9`_j&2!$XbS4^mhro$pvJe1_fJ8MZ5e-4ggR-v z>E+G;@ucE<-#^MkdnX|?A&KznJ<{!cD8wVB^DO?CAs)%ixA>ohc%=C+#^%3<(b__- zRAR7WU-Ga+E)J064oS;>QhZR3IpmKN*?n+6i?G5xMo#_leir8q^g2|ty>@$z!r*WN zaJH&ze3*mtNpEuIsd3)FabX00x>m>+)s1$a!g85K%SIKGIFl&~JXxygrL4x6&@T2r zzkhW9*#7bT&)IaUN0N)Xhm$XiY^{NJ_i&X02gYmnjx9PcR@428%jsKG{ad2F{$^y$6jLLO}C>_yAsL&HEOM(8ox!rhD(@wWc&U1$Sp{ zl_|Zc0$IMZw%U}E6}axbch=4~rTPja2v0VpbI~TZv)zs)I-cT3 zIE5NzrjQmoJi2Jv^O(J0>aeh5W@c zQO|~m2Mw_!S&O!GHlUHM#}ZHCJui9mesf>dzT^?KjDJKM)FqCarZj%rH^5JgU@;(x z;Qr)7hQ(@Z;=2fop}}m*m$udM>Oq~TL|Dp{&7=4|haYOftjBK)emVTg_&tc{KMnc{|m6nCm-Yi4abxRfOr_c zm%9F!c3a(jFo`vC_uYG5cx2Jes)?10p4j@rWA8c5m}jc1%%UffFU*@y*S;-@M<2wS zjqHVPw6d=R4fDR_i2!~j#vn(Z4B(*vemHozcs*rE`URtv**tocIPbk zug`+7hrqSVPZFEV3BYCZO37UUouzEf68J2Emk4~Rz-J4*USLW>8*_`m=L*~} zuo_UmN8ncr{$YXZ1pcVNO9g&L;AH~;qrevk{0o7X3w$aXT-jVG@I?Y&B=8jiUo7w~ z0v8`4-@3iu2PJ zzYGc}&cK*eHY+t?ke_|PGwjGVV-5+8lI+0XST@@B?h#n=zZ7`2I2J)zzpQBUPJy)z z5aseV8~h09U7jZbIHYeoBcj4yYnY{q;*U~QA14)9N6$S9kF65;<$e0Ux-uIy`2H-yh$pD_0VF58fAz;NUEzguAS z_`bl({~8QKj{Y`*mH!_I@CPtFmCb7;@OK2(cs60EDvS0AW4Z)ZeBylK-=Yruclkdb zc;@rxC|XWvl;qn2r^WGa0e-NK@eBxlufPJDQ!o^k%^l)+wZMwMMc}eJ@ZY6Ngo6h_ z!#pejpFSUiJ$D)7TtiZM9DO73q`!T=G51=S1b=7YCouk9gdw z?GiF6@H&CJ1lDo;PJyo!{Cfr7An+#z-YD>w1x^Y)F7Rf7=dWN|*9yE;;2Q+KO5mFW z{%wJ`3cO2T?nN5&UV#S%t_AzbRwK~cF&(1sBQ_;G=|1pcwWPYe8l!1ax!IjM^AJR)$D zz~2?PUEmdHB4yJj@O=VvMThl?z=ZU9+yQtyxD{P}xjEBtEy|}8uzNbq645)O_;c(^ zhcvG;Xr}UzMrUc_&l8!m7EEPRAbaWyIuBUclofGBZBC}qoNwdOe>mYooxz6KPnVUVl+K*D5 z_?XRje3Kn@7`EB$@%R?=ZV$JahdtbG9syjCvY&5I^vSmgywvc(f@GukVJRTb82*!< z5Bcyx{N5#e=rC_Ojaa7Jp@6`bdmeUTZL`SwbVAD&x~V*!;Qsj@-r#5HPII-LpetcdI-znt7Jz8A(?|2gO>Z38Q zgg4~FOuYJyw|V52c>1_5lhT|V45by;KwH5Y)00j&rl3SA*VmuTtcKP?-h#yu|H>=! z^6FUC*wEb8+}_mM+}`2Awhoi-?cyC*o-qq)cm;EIgmEPFkapLZ~HD?&GfX?wV% z-sd0$Y;GjNX<5Pvw~x7PTD}^>cs$%;3cW-5qUQ-t$JaxrhvcpuyliTOUC$2w2g8+g zTDmwE_hHr|-Hz6lmZlbvE4zIKLZ%0V?GiWOj#bp7D>dn(jA+p?JZ%FbZEQi5-Tgzb z{wWQ|HV61M!`EbnO4Mm7UbZd3tDRoLt z@6tEG+YsV*7X)0uAX}*38=Ev;=Nl%CI=ZpB0}Kw5ot8gT!i_B*jZ&^uN>q*Y4dP}6 z^wdKSr4$i7zXW-KF3Hr@Lqq-AK`eh)A^PL$xzxL9m154tZE|N;8Zu&iU4zFdWWl`c zVCL31u@*QAy9>-PUMDK{xzOG)Yu0-cl0UoguC2NJG3xzTJ`XHj<10k}HqqC=0x*1}?(3q0O*J zR^mFIEk)qri-*`@vc%DA356Gx9$HEi9cbL>a`akEC~}kh1Rh#am_*EHd(d36zOW5i zS|kBAX6Zp#eO5L$u)M4el-Mdb^Ij}9y^wr5WLzVQ&ALJz+L19+;m=YsAVg$f!PUhF zC2KxD$mn}C%V?7cuq>j(#=8BW>&o#9n&jB%z}xQJ5DazOKBDb5*6ru+%jVJYM2kkO z+b^3hoEYVMCu+XH;vx;5`(62-6E;=z_878Xp<@rjS)`zkJxmv)P|pVo0?^qfD7PME z$U+WiQO}1FFA9obi|QVwCC0k_va-HC5sTbHF@q+8T8x(K4UqlRiGv#9k7q?a<1#G0 zCe5|XiD>s}Dk)6i(}=AyRUF!phq`ybI}tvlz4?|+E4Uw7-awIX zscwh_Fbrm3i5t2atvYG-@Av*rq4XeQW)LhZB?y#6TdmT}4t*svTxx*|*Eb7#S;k(G z!A)C;h{zS19x)FtR~p^rGUBlsaX7u2Mx@IqFCt5_uzIDIVI+vnCVZ{E0Om#R=xAO+(2UA$2px%jkT*VFvl@w`@2&oCa-fMxiFOh zxxV9FfJ}|C*j3m8p5oSXY}*lr?J%fi`*Lb45Q|@@#Ju-&BnGJUu>nT=8dS34#Jq3Y<`aB6KjR3)L-ml+=z9+!KvOX6GFw( zG8`(lsS&c6eRBI9)g|MG9bdR%f$C(9#i%)@mWF zxv~`vWMHG&;i!!sNGw{~22f;yM9==27J zj(3VM(3Z!(iC&1NluO}rFFU zF241o4yFV3uuiRs$r1}Vm6BJS-E-`%H0`RZERgCKJ!5o-VL+m>7qZxJ02*1hI}yO^ zLQUg!S511c#v9xrwCrH$G2G$|Od3X~gf_XTsPxj&30~-VVf_A>P)GQH1-t;n3}crA z+U*e1v%nii1Jd(=$QhVGJ8s|txNNf}MPR2_Gl0MbY(TfSg8}e9n~cL&EYO}0nxd-= z?uux|G9$W~Vv~$G!I*5N!!Kk7eVVcE3!B640oxk5%fd?1Z55a~^sE4DH3OmPLm(|D z0@H^;+SX8MWf7ZF)2%E*1;bP`i`YbJXlUfHD$Nzzm~L|UYohW7=8xJN8k@uXMO9Uq zD)KiyRYVcnc2rKX(KobK7@?Hoa5Rs=2xZi`U7g&R?RQovZKG*sqiJk@=~3l5SU|I$ zHfD?$wx_sJDzdj|t39^uh@nu-L;i-g9i4$l85Lv+;&^)V;d zb^bwYGZ)ekQak@(G&cIa@0v*vKQ|%K@mg?`WNQ4r^HwAf4YWZDMC$O;VFt6sZG}t< zJJ9K&JQh{?p|lK+H%mHljMmbc&hjouSku(uB4QKAufs+)z(YUDEibZQ>I(drfjkl-&*00^Jn&6- zK|aEJws63-(dMWEhWVBW$rm+Ofp61uCS*I;leb6Spl`!)%##BtP83aBlO8AloyYxK z&lV_Hb>!I)59K!wU)WjU5ivOL?wPVi?Xe}(g`&MEf%bsCJ427PdYZuigZ;PXq8*^M zdSg{Qx&|!!FvEwn(IC}PVpLU6H!H=7885s;)&B&sQD9x zi`U@f1Y1<;bvXy%4LazLs|?dO*jM!NF_v|kRx~w{bVqjgw$*6^{lfTfRu^^;6uY+J zUCisd*FnYXuA*6;=_+=a)!PaMl8_JzS_d$zs}ry_=w}f_!dGLxzq1XI);OK`U<*- zkw@wscaO^kHJmd|_k00#w3p-_M@NG@cx}(z(;qB=7t_tv5pa8xW$WwrWDHSOP zk`Q+o->t7TqPyM&b@4fO_;$MT)v~VgmHzW=%4(E~?l1+q9TzpWd1ihXE&Lr9o&U*G z{Hbv~dgh@NVLq$PPbWhPx}BbQhN7F={i$D`b#XdEQ(rvw?tjp_sZ*cj+hEwnO86zhKwiyN{1kDj{N{s%tP)c{Lu|KsY|{s+ZBpu^v ztpCB9OyDtEZ2tr2DefvsZ2tqhz&I2lKd_&Q{b#RpfoBceqK@@H^p`TC(O=X66uT%q zf*sx>?mafBz(`9gjyXf)8?v5bJ;7Hv=z<*=yH@T%*5>;_iO4v6(Rs&AypAr-nZgDQoz*K z$b;9HT01fUDQ+1Bq-_yttp5ROiq=x85?|122o)@`{s+0jM%_um3*itql|&`R911SS z`X34fTnW$EyZWKHzeuZucsneHQu(UU!pW9XwH4&@aUIU72iP{$j|!wka}m+UwNG3y z20|}dN@{is10pjN)D)ENU)juh}KoIhT7Iu$X22oWC(FRry%|!(du=@U03cSXmyLt8F?JI8fQpik-?8EsnFg;0me!+r1hFP(pGeYB6XZGUg1Dg16=o z3>}U78*Inoy>;=yDnNX=;+5<5UWlV;Q$Su~{SQN>HGLU514`k|A*7AjAxD{SRz8BI&J4KVtn4ycji9?B%u@StFm$vHpix|AXvo zs5+QP*91RV&`O#`eLyR&EOzy1vyt5+sKvfq9w?_NlGI-`{&^#lhqewy1;2 zF}_BUWAl)2a7(R{K0VY`MrCd-?ivnZw1}R*5^m6e}8#{8J!#(Ngf$ae)ETK)8JI~GVwq5 z?m1*i)kv~_Wb2{f=MIcjC7#>2*f5@b)wO%a>xYwvCfFA1_Z&0DGxjAH?lsjT z_&;7fVuq9B6Q@l>?LAa8SvrJxe!gY0baZ=S`+~{R`1pEZQ5{-K@<^ighhvEKKOCje zDF5YGY(+lGea9CiGCe;2{>muV9bc5SjB-+LoX8c>W10|1*B#%A&6bdB$Wuff`w#~y&`%CdPBgv!lk_*{& zswO&sAGx+BaRA7XtqT)R-k(@kRXkg%7k$1~X_3K|PY!QAGI903R*$*kKm8Nj*euHN!wo73ofu(PCiH4%aP#33$`39_8zM4rK+^;X0oLhB^mz^ z9Fjow_L}W%pj*Ztnu0pKW&ExwD0JEWDX6h6<8O*kCv7*q4>10u;(Fgd%0zoH6>1_a zC<4;$eJI2urSmNQmmwa>&A0fUg?Oa-FUIB{-TUYkTd0*v40h~G9(KsZ0dm|SCxhT) z4*4TRb|0M2BCIfvkyC%XpT&6ty$;oEuiajwFgV-*oEu|&n1l04Z*u0T@e6`50zX|V z!IGYxj;VIxtq#{fguZ-NRSU7j!KkC8!0#eOFg2_Q3d(`>sCOM#u)voZP(! z!;~>^pZ~)L@Iq_exA?CC0IKQUdwH!X4Nk$`SzBdFZ>m6+@2sshrDO%Jd+(jK^G&I~ z0tv#CO$jSBv^_L-_#GIVhrxEs_{X)OPb`q0^uunnVf78NYqP~paCGmUqo#Pi4#gv; zqbgrDaT;919NLeLtmn8t@eeWso`u00Q4x3b!jY};Copk_plc8rv1ICSl}ig{mmYTP zv3^v}!SxFlKWB`EYZs%hSy=SrDa^b%5_ABz8T7j`V|q=U_1HUFy=DAw>Ba6{J#sB) zp^^2~!%yD*T`ImC*-*Xmx%ueIW(zzf>m_0d^hZ9B#K!K9-pss>@AY01rk4Yv^#bgY zVtHx~j4jymVu{*dY}}edM6dw)AM=U5QoTmbWYUtok@dCUpW~ZFf%%vzF<^r4wJV=H zFurhj1Dc?4z6#kqxW0xhuOga-dq){l1+G?b;1$}+G0xvV({BMxiZVto@%1Fk22(i5 z<;a#%H;SSXEWSwmho|Fr5&z(H{5y!>H64E+@wZOL-%0#yrsK=R*DF4n*!L~I7c4N3 zF}DY+Yv(L?D!OuLsTT2lH7VGH;xRz^O$s~7ik%5lSk3LUMJlPSs6#3Rw3m2 z;pB_thwKjXb2nQiW`c2K?Dq~P59{QLuaNrD*$Y(>2})Bvb7{7)G>6TDeDS*mbKz`k zVr~0uG0<}wNG8yi80g5tnX@73%V!ftry5k?Yqj3yQ+=hcMY8US7iAq=p3kFJ<;PI? z+L===vj9h~n%Og_oEgX`#cTiT@Q;H*S&e{vzivbld3VYy!3lrL?o}LWOb6cr_;URCWbrE#!1KHK zl?mYa2!3S(c%H|vOaKq{%a#Q)=Gp-MqX7O%0H1@l;OI98a3O&IFn~WDz+VgCp9S!x zXs$la0PYLm{Q>;(07ll#J@{RIDfTjeC(P&6>IkO$asI=zEx?ZkF#R0OdGluud&eyJ z=V!q;LnW@uPZEm3W6ZoA1zgplw6u3^{|1R)Sfxjs5GJ(G%@C5?@QsCtRpH{>CTqy7*0$(KX27xaY z_{{>Z6u2lbDgw`bfiDyIBLZ`%!!RQ7YJtBiFtzp?^KSyPL!wPW;c?k;>0!)80#hS1 z21 zQ7&(@!G8>Tm*<}Za7f>AMnv=O01j#XX%_qyi=(h1{X?_hzi07hydq)Rp!B-TKzQDY z;h=0s`hviUe?(wylYbfDpTUq(HU%ZZ|C#vkP0+ZqH=%9_pT9m~h5?st$hTm)as1yS zuzLJZVCDZp3`35-B(U=TkpTbK7@o@JH4^yy0&6_4!%$T=H;5xEu;S-K!FSobMIHF> z@_zyF%;(Qhw4%@`$v+947RSE__B%5$-7?0hV_Hbx3Owm=Uys$Fh3WtI7JdTb-z6A=%jO!Q&6NUAA6w}6 zIRD|fQD_wY3xRb^{!)NXFK3)hN`(I|>>a@01NT!Ha=$1vO0pKiz0<>eoxqB}S6~p^ z$Hnsj;2Dp59Dc&$H={kSL)R@^g7*oGhGQQ`|54x>&&NJB&-_YgbZlRYB9)D{r8NS# zOUTUvuM;>cu#Vehfv*(&2L#?A@TUddDDZOvCj~wv@MeKeS;4ff75GAdwM}jm_$I;M zD)3f;?-ZDOLdJYR;6Z^GU&46q68IiWhv+*AIF`0V20G^aR=b-;B}~;vcaH*=XU@vH%nxVN_Qt<_jH=Gy_JAY zbB@?FP?~dXXwtvhbO4!?IL|iRFuhaYbIeh|q#woaBm#KG@Y7kE=_-7m{AEU08N{zL+WH8uwxb&1 zHRcvi-{42DPFR&ldwjF$_i(Ej^6*;UmpZ|PcX|93W*G3NkpD9BNuE3{@bU_tF#lf@ z{7Q2KG~W~W5_34Ee&vnuaCn*<~QCEUY(13~WD-PvohT(Ze*LV%Eva1l_4*_qkhVP|KC zd1p2WpsaWU1TWN2sk9|hi>=nn=cl5U>WYeJwOUJUzgjg~FMQ&q`Ls$ENdN!mIp@4* zb~lnVNn6YN{&x0#&U5aU_nhZE=RD7Iz3w|60nCT{4hIhf@KgYEf8oM#PvKzhBOJ`V zgM+zWAk6&&X-mSSElESVFr*tDOj^;wqz?&`J|w;8@TBV;OuCLR={nMH4o{kmFljc@ zW`s$bkzR6PNG~~466V^<^^P#tJFZzS4A&{bT&K7O5#}0Xux(g^eHq6&pS0bhDSU#T zf70ee%vkzx?m{!mlZJJYw1-=bt`baVF&L9B%<1Np9zM&w&BN!K_j|bAJnG>t^DPf| zo1c5Q#|RS@?lcO{Lliz~v&^JCyxjDA_&hV{;S0>g9$sOt@$i6oIpD+44&0M+?p-YK zBAHJ*1U}Y00sMf#$C>THJcA)C&vyL3-lmBz5cqP0UxLy-Jjb=IYblm_FlgJ=218Ju zG-g-~3k$Ce+Wcb7#QHG(HvICE{yOudPhZz`hDw{u$OV-rZ8rKjpK&pi%}o;bO7o15 zt7|Tb zCaI){CCzcO1Zh4l=@cc+9}0Yfr1?vM$D}UvKpt(w>Y}SL%RG)$^huj?u;y0$h)#p~ z<=QZ9ZsgFHCv9qGm4_p<9`IpEe-i2Q#tXez&dc#T8Tch;vuU85JyM2-lygAfiJ#2h=>hx$srxy*oZtFvc-Ecpze(K(%wq>&9$UtKyg=%HoWK`KKb|4* z3cn7f#d?}6b2?~~LD*1p_|<;rls0S3SsuSu?&;8(Mk`$8@t0zi(4&X+K*L4)-ai26NhyU>-Zo+m1|w$9}aYgSe*$e2FsxUc{N^ejldYJmldH z^GOePn#VodZGPb49`iE~_nN0X+-DB7b5|O(>d79y&~naLmlft*k6&q4d3d#{dw7kx z$-@_!+daJ2yv@Ta%?CWZ!F;-zwz)(&0%x2GHLTN zv&h3QHz#{IW6t$()~xpMkXh&9oVmioc{AkU8w~GJ^GTaAbDM|9&AlG3n0I@)YChuO z8_j1tTr)g_)pW+qk3789?C|hS<|z+Pnits(PZ|{YQ67G^;hC+b^BR-#@N3O755LZ= z@$l{DdJo@WXp3tz#_c4=C)e~l1wK*M^tTColJKv#34F5bH~&oFR`X+o|B%3oW$(!{ zOyTF&?(Kj_Y+hVD2!8_Mm*5uZ{qx;^^Dg-4lQwq;`>%UEw~}$eS}tuhVyyBTVp*R;6FT%qwY&Udvk334DdEkV4UZFBkrZY;tDoZWt)3z#eeQU>X^iO0e3(;>H8D*G6Ui_BN6!>WQ zmM;|e82OeNeG0$uJ7rux{MN1~fQR403AlXZz6-(^AsvT*K7SW%2Ywfaf?vz;;t@6& zp63J4`Et~q-vY-&39$PX8UpX>x3H+`TfkAYJUpw4_cF%^yxbFHUlWdV9ost28F>o7 zaBTMwiHBp`!5qUIQGOGs|IO`{TCzByHZzjTr7|$eUM-di`K1P?;vwHpT@zyX5tjbW zO?7njcJ+1kboKRnuos5sM`3cFL7^b0Ca?bX{*DxU#8RS52>RFP+qvt68s(@sQZD3M zT@)xXV3e*i``g0@uFhWn*lcD*hV}zS?%tiqO$CKcgcT7q9WZteVt0gjd$_;dmmmae zX%ZWeiH%68i{v+9GAk1sk%^5+n#gRM)zYHq*4=NmGfTyY+Bo+{&FwBr&gnW^+Fz+t z11OuNk+CZb^&3Lh4E{saYlABNYSwTgHGZ~Jn{NX)75< zN8^zAtP>lN(1+FStzOoP<7)P@6H2%`q4y4|y`c0h*M#qK8jAPNbe`CVd=4v`>XorV8H7$s2_?L!v$mtXOO>e;tLpACcpI2)5CBwC zwnjkq+VcLbn{90Emo4kZIEU(W4C=o2cGzjy=d+PktD3Dv{M@3eQ6jh*TZ6Q;XksH0tysx7 zoB>jrtAhp7#6~1dbjcV_Y((Ny!Md8*h)irm3c;HKYe%hqk^sw9$|T`BJINv83nbj$ zFlpQ#%^7q|Y(yqDBKMSk**u0|U{N@GG};IXA+ZseYin+x#71P?BD_5U`V#{NrfxDT zw~CEd8mGbJMqWw#ne^yPWd?Alk9iMXocHL3^MCbPKb+Wzv~7~uh)irm3KIgGH{7vN zJ5MwPnb?S=Ma-@~%o{Y78I4Bun(Tx**U$%>VfgghDP_Z`pKUl3an`}Klh}xq0o5n; zD6Y|lb}(rT5`#>zasx|(5}VkFC>wA%nXQ~kpqz~i^N8x zG-zTYvT9GVF*g2#HX`ep;YK-!xl!&>;Xl5Dv+%;89VSN$yJb1J5nboYS6h@=hs9|SPZgOs^i%zS{;;ECR;0HeUT|yVv}LNrFixy0jkzftraGu z3*aGRNW(C_=KhNZjEqMeFgWdE_hV?v5aOU<#ByQtE{kDGa%?P3hCD}`T!@T1xiOi- zxH1!3Gro3JAS@Sd1lt0+BC0P2ujHbdkJ`>bPfmt(;+;_r!r3=l{XzO@AJA`+=rf;$d1gaOd)Xh@PEM_SrY59RO zmVR}>rK(S=2DFcv!T^fj$L!8<@EI5%>ilT+I*Lk&haN z)Amw#m^1yu9O!nfDAD0|t>N5Gc-y&Opn&~97qtk>HP7szm8Jl2e zlX9BGj-6DK7?*0t&QZ8&YR>ti8T`Zaw|W=bZpY5sr@5zx|GjDc+ori6gqtSC^l9-b zM(JkyAF)2p{qQvR=cc*;8g5!FV#^+entu&%+JbWKufu&8+-zC;e*S!_jg- zz?&*rI5z117rd!H@&ne*#-$ogj#c^}0{6oRf4y}d0XNlRvR&zaD%>>H=JG!sZmLM4 z3K;rR@iy%*Ie*%qqk7lt@uvR;c+(<~^Ir+~e5B6~q(9Z9(i9;d8epNSRH`H3cSQf| z;ik%Is!yhy3caa951)&;%WzW-9aXc@e+%BUY2@;|1#YS=r5aND-wF3Ef&UxfreP-+ z{vNog+O1;4-w!ucsUV6pamFw4Ek=Zb?W-v2w>Do1y z1;}-S>(+0)CbRak)$0T7MS}zB!Ht=918dfA;#zX4OT5t1j7e~~S}>XCqRb7A@v5U*>=N%+FF)yH;Uzsm zYN-|wy{77x=;?HnB|C-qv+I?h0&0$KAu5ELz*0SGBNgjh!o>kn(PT18{(=}l4ek0P zfhAIK!HXV_PY#nFMXNr07}QuH$JkZs@wWrdJxC$`Nz ze9$CDEH*`~-YG*8wXg_0lhD9=W0GmK$6Wb(W~emgeL9e3s@vr`RI1=+t7HpN>2p$j za^`^yvQ6Tgj;)OvHRBWU&;P)<+8X7sFVEIS_M0vRb`pjzUy1l@bRY2cB2iSeC^@(hnlg$5*;`jGcFM<2#6LTP059Yd@y}&`RP;h+ z3HV{{=@I=HT6Y)=OEwlB8ZC32` zIy#^aqpv?#%0eJk8cLCUKn6!{+(CAzA|GBRfMSYeJhTiHQ*?A&#T1G7r^x>4Mm{&1 zh<_^cqE?6swF#Uv-bQml))iVENe-DY8;Z{46CpUsZ5o+OvFEN z-$0twvUsptisTBRLo17?fm7-ZTpT#R(b3}?T~O!@uJC>Rww(nDPLv}1ao<;^xxt>U zVjET-sgK%}Eo(CJ0^~7viP)kDn=r>9FT}Bm&a+??hh>#3r zuKC8*rM=+@xm}}5PEsxte4t1LG|`Kc%;Y^mo@{wqObVE*@OBg@8jgArY}q>MJ_iiu zbLo(H#K;b_j*GB`YwC!u6lxJP)3|H=b|Ne21q;D)8C7b(kV(WpbyE|!F{K-4ZDjG7t(BE-lI1CE z=2(_5Y-XN;<8T~uf0U}k%?areU^~HK5v=8>aD;P((*tlMJgUna@hBqoA4`8%M=_3? zy|5};FVT18Vsr^>jM>hJp=?`vHN{B8KUJ0$pFQqnRD(g{NTF(GT6YN{XLd(0FB#lNhDM+M6bc_$Q_VUbzVxLZ$<&COr}VbYCE@5{ZzF ztfaCd=u5;uos?rD{z)o|91XiwbPbb;eOiuS4urq+V*zU5O>iNEFA2ZEHhB)6Z;^PTShx`t2W_#g)R~NNX?#gB7ASV=n&S@>E%!+(+idmJ{qEH z?$RhL&(l?lFNAoWqgs44(d3~F8cE3AGi5@3aRV2c@ImuVgdWTEx+)VYmo_KjpWy1^ zhClx6ttKa{nTUU8NBm^~X`V#zCVyJ zOT<4D@y|s3lcZN7{uxXQ|LNc-urN8R_9wnjt=+eE81w7Z#!TAMbghZu8v~f$54bQd ziqm;>0ACH6eV6`+EPM*&&+mX#H}P*6GUg*lbBQ^^eC!M}^`&LBG=I3LFksSY;N6pk zHF~b2eA4D9a{}IfhwzgKzY`DfOYl1hzsDra*D}Gs%-Sr~1;R&1q(-$Dd)=diX4Jm50wZFZJ-bCg))o zkN0r9h?mouHYu~&<2%hOJlt*G=HYhp9uN1K|Lx&r=5rqIGvD!WzxjV2KHu!{@CD{q z9$sM+Bh!chEGLEl|%PPBp2<|R@l4RftI*W<4*6HHJ5le zCu$PtOq;y9&f`Z!or2ue4F`%hhJ@`Jp5YoPad8$Klkt*<{1y) zX%3v@(!a}`>fyUhkB9Fv*LnC&X4u1TF*kbn9<$lQZ#BQ?;WwH0c=+w+!ydlZd;)NU z@iS@GnJ1pzxl_mOW#)T;-zNC=W;@__3cSI58?cV&%gsLk{&T@^G>-$;vAxNB6)N9}1nRm$8{wIO&3+A*xKn#7-=0X3xq|KiO zWAe|;89vN=MD+xnY4hiz7DM3=2J_E{%?2O-qruqwL@=Lx%G~b5Oqn-%_%r4$9{#L( zyN4e$_j&jW#;Gw$o4+y-dHk2mCq4WXX(Kw*=BwsWkN=wazaD>24{tZ;d-$JC*26zE!ybMj82>*qw|e}K&6_>^GxK2&|J;1s!@n?} z^6)Rs*F5}``DYLR#=O9fx1Ht?51W?5Jv^u7G!M^fiO2j4TCVl@gIh8leql@1!wXyP z_VAG{Z}RZbE!#Z2sO2FKpV0Dg51-WXWe=ay@)ZxC)$;cqe$f2f!>N{MJlxqbcP>;F zVXRJ?Ka_d?R|5Z$%=7vk-7oX}0a)}VF}i<_IsXWO-)VjfxK-c>%oBihz5ZkKL%{kS zy~}I|%>JFi??3{0w&TZq`S3eB2=F;bXNl=;`D&Y)S`K)d>25hlLU3&K+MXcX-{R6q zV`RiM!$3=`55KbIRKQyPz80=ugHq0AEnL4CYzjY@&UV13;m3#Nac~~+zoI<|q4!5Bl=3=Zp-^TfUs5{3w$H035yK(-Iz`GjfM+f8l7*R*Y zVU{*W2V;IwKyM%C=TPp;E=1UHJTvYTe&Jk=;Vuuyu!A{Pzln01HLF%FZ#`qpWmlfr z+PM@|{qMS4bz|-2QG+JZW=3+k6r?T2tHn|wkBupApVsBl|M@j}^`D#S=<4n2>+I?3 z>-S)9zsZeedBK$`BPD2xDkeHo@F`WwBdsnW+?!=WooDVcp+=df?1j9Kf_rt0LY)=i z16OCSe{420BIB|xBX{pkO4;1#fND5dK|TXv>!>fX=)XmAZWBVWiR_&CduZ+}mBcV{<( zYjgV!(i zEAf$Ry|CYOW$hE?Ait&6qy3_CN)*&#{>Ya0SEEyym5tuW*hz<1tRN+U|By~=@OpfW zQw`7**l4E!VgvVGERJ#-Tswf{_p1RZpusi4h)o@*tdMdUiscPthowY@U?(U7>9Cj| zEH(^_2|=;QzRv}vIGD@;QYw!Y!4$0oLU;7I;HVYDLo2(44?KMJ5FSz^wt~2c#qiP= z%^q4yR2^v8TyWvFnvh$O^aBsADa1i*xNQ|dUd2bz+9C@G+vUo2k7s?YgbaMbmf*x^ zZ?~CT*l;N;j|0tNZf49k`LmWx6Y@GxF6gp?ibJ0sZhUz(>&VcoWsGGN9X3&G3GI8X zy~Mr?sV_5Jt-waTeh!^FZZRaFa52I~LHU|Gb>QMF*_*{>morkL)>34X(IehkQ=-;# z_qB`ddR!%HExVN##^$A12RT}5P*h%!Dq^(vROc9)KbALzBT;MFumZ|3xj-7XZAKI2 z+7h*vd#0r}*G6rf)hskMf&h#%!^J`=52XVs)q&jC&O7aJfMf_HW3e9-dJ7^QsrL4M zJN^+2Bt#IbYncs(K<9KJU|ExAf}kqS(p=8zX_w3F)ODf$b$XD%zzY;nMCHm_CeTk& zSG!ed#ZuW4NW9E2ta308Xemh>Vr#%*NO(cYz!rQ9z#Wj9W@WULp^%27vEi-L5D)hZIjfv~!QrQ;&U8Y>gE zmXzk9EIOvHLSzMth=Q;=Q9}U=jU}N19hQk&OE;zxwU&_>!IK@olMizuY@Of$_dzs_ zLi8byw*QE8l41{Gi^^?wF2v?nb(2yS~b zQEN#iIGRe0XOa)-${9#*gQP^Bk6!FjU!1Ue(#p)z_> z{bH4o!O60E#cCgUe6Aynv2d;)7lY^L^zzCgeKFh#y7$>M>h(yvBXlhW5)6tcw+EP{ z_l(OZ^c(%O){K~_wS?VJ(TkI)wT$(N&Qu+msI`7+)lTz zP`aor1X*XC{nXJ=sV9_V*}ZF`)-pwQYuF4_A!e1_0`1j%@~D7XZo$3*I#$|9bfVT$ zHNUZqw1pD2mQawb+V#*T`e8cljfq;zM6IRFIx-+|WA-wyXFXACDYGmmS=H*8sI^o} zG0zLNmKhvy*6NMw^3^yM0q=v{{puNVWCwN;J{h27;UyAVXdpXytv);~g*|#Tu0s>h zfYl5q9Y_tbX!!4xPHGF7jxT zD}vx#0Rdf8X3B-l(p*KG zRZ(x(9#TT-B2(2;*bbejNsAy^mxUCa4qopT2M)YVHP7Q2n@_bKI;iHAj4<>)jRBb| zj?nDIII#o-gpUZDL0Z%zmtrY2ZnH^@Sjs|b%udV~b>I-LbLg#RpP+2RCD)2BGf@uZ zT14B;RitVQXeJtEO+@lV2?|?m879O4lx-3{cecGWTxvw4_0o`U)hIiGI@qBRHr{wJ zf;eU%ZL-_D&0=!5Ah}twAzF#ae7BbFKyC z%pBoxOJB-`b2p76=BZtJOa-~ZDi*xQv!zlcC!Zbx8HQ@6Y1lTfoZ8tYpcN&wCCaNT zVvGh2%yEX!u-}@%nzAIQ=9gT8OhoIfLA0V^Nzt%UrWj>7qG^b6`LGTPM93gp&4LcP z+#Dl$NlZqw2B2(2Et`wUfx=jnN6A<_P3DI9laUWebCmQr)wrgRVIl?BQb>S&p)@mq zHjmT=6_a@O>!al=+uoUc5g%xFtM6lNA#D}u5jQNd5&9+@WyUacZF2D}tKDE{*-RdD zv#q#}bDf0cLo7QCz9b#9RC3V5FAI zEIxEZ&XfoI*dO_6?jdrknV@u5&|18xBn^ODNytBRMZqN|^hG3LiGemLfYAA zpreFRuu~s~52hm>UjobcqVcjEvu)L(yt;b0`;|cR!nbc#q6xB3H^p;?4!fNkemJX0{0}sGq6Jw8qNv+{f^k zCddDd=CO-45w#mpLzx6!6@hYjOB95_EYcLj;WcIk6N#_6^RiKz{6up+3W{-tc99#e zScvMQAhZ0W(>yc^MWOxV11v}Nu_`oU%edh{#ribjN{X=&_~250mJ}W8$+?8C=%zR# zbEZoh3|DP2a3nD)8V+hWKZr}G{O1P7GB zw*sn8#8x_v^EqadK*Uy7YM`;8coAEn5`n{TiA8K>r2&#O9w!f#G*r0>#c~R9SQR`) z*g(Wq1SK?ui7>2+D$2CuL~OZg&LU!qI#AYBD}f2bNzww(iq90Wg$K_IrFsbOL~McP zVIk77wGKRZQYckNcqd7V@H{1?)`(^iTX<+q`7tz!*urO45nJHpfKZYou@kn0irB&n zEgy*3qK{Z@_A8d#pzSGfdqxMsN7O|N6T5LEGAeK9kx?siIED1Lk&_0 z9&c-kG@`;tbd$%ss-gv8f5M}Ks)h~R>3cV7LTm40G}OIhEj|M6TBICH)&i^DijfdY z)&lDXF>e|}7J+s;aOa@A2WE=UHCU^qUjiO4t=39AgJmPEagqNaNdYY6g6xq*9d<9o zP8QKH7pFJskgA&7K-R&L1WW;g+>jKAwU)N(nOax`XKD-Uu-y4$!ni7JMVFAW9hH&} zhNKf-5}!(LjFcsMhz9L&xtd1+XbCo);L&QHs~=0hN^3fsb_kzq3UB2IaQ6bKK)Q?V zN6HwgK5}o*B8=lrP2(gsdQE zi5UPk>8XlDN`n+o*+Bl1MPxSt+YjO!!H1i+eij8*3Kv~ROslJ^pEI}+H#CGPA(Ihi zR++CI#g@(E>ZDt)H`j{wI-EK^A*dyKu?5x0S_09DU^7!mIi`kDEW7}l@@T#!mTz3z z*mlBxq>##J_CTf%$2lrghY5++S0pE@mm!Ex9V1&3pU}#>S`LppMhj?IwEvmsk7@X9 zO{fvM@yL>L6K_*>ZJ~L%rqF0`u_e^Y%Ku5evt=t2rDTLg#z&0~$c>YOrGP7A)DhD& z_yl|rqm;#)8_@HT3nNyy+7yJ;2cBejy~3~x57#Qf5mVQ5TS=%@xZN;I^*`o~A+r>U zYsNt?BQl)GRdrtN#{c6HrUgH$%&J3s7JjDKj(_%9N(;~d-4YWgGQPU#A5tdzJ>VF9j2E* zxW$|lAY6I~HsfJ?6FJxB%63(^=FIj}caR_ub0v5B=rh+q7h&+smEMdIbIl842cWs4 zfG|eQMJ>W|(j);iC;EqO)^&89nPRv(b)Ig z7n-RD@Md}k;@vpWOkHIC4}<$#$D65NTK7?K_Z?)W-i-!e_~YPih065nt^Z=UU-d%7 z$D96V;N5eenR>+fFNGU%r@my}op3KX9V$?*dnMdU(ciaQ_gc7ra)g<}G$QG3f}1M8 zsV14}XW;%K;&XnYy9PJE7mgpgZ-)C>)aMuseY#%-clyO)#8heefOS(1_gjPUc{AKpxlfhuOz#f3d(oap+VJWYXNTqD4A!9N*K?dM1npw z&u3)Sz|NOrDljkVAi*pdPr}@X2d%H%OI91L>RSX`V(@PbOX}(!aP@dNC;1eGAO9MOchCrajDJ#@q#h`7Szw$Avo`II6M^ z!*Tv|j|+DW5MdHb?~p@h()!pcG=;ktiz(>?_qa4TXERUtJce-phYO*7T)649{>>ot z(=HI+E*#&O-u<%y`v#glA#Km~koDlYKAqOT{$kJoz`Mt#=iK4D&IIr{TA%HzZp}G% zhc#n(*NQIuzNhushwdS*i>~nx-ABQ74a&>D;97Vx-XB4I-euiwc(Z;3)=gTU^>J&V zeCSv<#tK`9@XAM{_%FxaGVe}10G}~KAhqP#pDl3e{!Bbc`@ek)3|f6w_C-?T5@G1hi*0+!?hAl|G02_v!=e} z;OAabzlTYg^SFClQKzq|M`Gdoi{o9Wc&(hirhes&-CI-Nb=K~!sUJQ&SyQ>j{%5YK zC3N$h;H{8r%)A?TPrNYZRon#kt=J2@z45JZpMbruK?d3?|ELvAHn@NcpZ*+@h5PDf8}`*?}z(p@RC>#ruUb4pN062m+)6`521XHm+&~; z*xyg}+VJ1R`ylWJNY^;t+Ia_=sreY}Oz%g)!=%vEPi%OayZbK6f35X@2JUB&zN2qm z2>iov57_Yhz84~WPv1=2+jC|Dg)?*CtUE$%$gsJTy|3O_3^%<@N;h~iNeY2vH03;@adYLQW*LTyN6}~u5AS(@6mF6Z+}%FE2}%?9pu;gr{zt^Xz}@YJl22(rg~o>$RT}u)?LJtr zdzAfaPMq_RhfewQ>rOb}iGRHN&N+*IF!ik?-m>cNM?U+m*RJ{DIrl3*e>{;~#bKqhb6mIBuCee%Gw=k3M*c@$*uD zMudIDxb{K)5qq}!A9L_uBg%jEbL-%xt6N&o5%!s5xm4WGut}!CH{;V~T0uRXVHV&# z*@s3LGRMf%yBEI~Fd%Wy@mzVw_h0?vdBdL{eAbxZSH0rd`wyL%^W>R_K6K@??|duv z7>~}IV-9_2@Y%Ts&~^8QmThmw`}R3^4?a7*(>(d*FMLK4IOq+72i-on^U#NmBERX5 zuYP*_oR+WN@yMsYJMYkk4tOJD&Rh65+lTKMd>VN?jXaJS{`>*TY6HLk=&&0xP$yrp zb3x3EDVs7<|f=ab}i} z9>Cfh`gy4w_G42|`~((czwjC4rR}<+h5Ss?uX+5~55zloBjEG!lN27A)s2Lreg!1o65hXR>ez%Lg3`2wFV z@Kplyb_eb!2#gIoo;w9TN8mpZ_*{WMC-72%za=ntwlI|{aJ#^VfU!PlIs`sNU@ip4 zEEAZMx-pjv%-P788wBna_|*a{^Z)k+<}MIt$pZHY{CRJ1` zaK7M|2z;Ty%LN_~c!R)e1m-_CeujZ(zthaUvAWnPEd+~PE^1Aeyhf7Ien zIS#x5%wLn{%o%a-2A*->Fl5Y+C5#3c!{j(=bnM+Gu;PC$@KSLcilR&^+PqF+9RozU z`tAz8AK_h@pA6tId@nAJxG;AIa2V#>yTC6yGL9RDf8#Fj+b#YG+#T!0Vap^V;kg#m z!K9t(D*`M2K7n;iel);8f+=HCh!71oTRZ#%!nk_1p>GJEyrRXt7VxA-Ggo7}aqYiV zVDQiSn*d0JgE-+cli?G z;Bkau85W_PJ{iP4;S{FX#-Q@J@G0OKe)9@rUTa|n_&W)R{X03 zMqvB6blwg;({Yc(|Ip&sVm$WX)19;d{+GZQIQDVj{|tDh^Zt*_HNTQDI=3%Gl_sqL z`?Ce^lbC}7FBdo~u+H1J2z-Iy-!AZgz#kHLmB3#Tcu?Rc1YRrf0cS9;bpkIISjXgr z0&f!hO9j4C;4K1QE%4g~E(^R6%{OUo75J4{4)N`@&~XWtL#%58pL8zamk7KVpU|W! z2z)Mv&ZK#hz+V=aRJ1V_H2tLc2Z0|F_*()W(?L9MkAe0R_@@GYR^Vp^Zcj1HfpZw< zet|m${;t4%0-u2)GHHqe-!3pII?yKq6Vm4a2jK1C9(?LcU^bd`DW5c8_t0GRf!2|R zLEoc2e7wOhm4{(;m1f)%CE^H-k;#2U0+u?fB2|eAo`} z!teFc4*haIPdrTC^CeFjYlmyYG-#VcJ^ljpfD3b>oafV(M$5WYIt@ePI@4=joC$s( zd9?;K0PB6nVevkN8>@BT4jcg6=WJ8iMF@b%>K-;CW zSWJmU(49_aV5r1cU(jQ`H*?l5Q{NXx&UTr2Vgh@HT}SpPWgHU|*pzMbqRqJU?rmMq zDxVMG&{^g4f#>b9nezGY(CcPUKY-Mo7(UPx;Kpm3d_FKa?c#&6^B%9^)fpYr0o8|K%@F`;JBKQe;SG=19_2FhM(UeVs`ap}AI?zbZ3+e+c zBKeATGVQ>MM83s3L4BY_AYak3JU;LukFWSZP#+$ye$9gV@Ca(>1@$5RYU_qFdO>|) z6*t-VRXTQ8`Oz}k~dg8IPfxNQ>D z2UZ6zO1)oZYD)iO6>v%bc)_tr2>@Ez$+1HUzF4Lb0N^MyKTQb$urNw6LkWO%({v>O zs1}qzI=#eB2>^l;J6#C?(9{i}_K{Ess{{aP!1l^)N&wKwvnv6B$E->KkW^vt$ndObZNPXBr$=V zn84)aF@Y^DfpOB?-ZW79w29~e(;pZ?(EH?r zQUP^rRy{F+otVHb*Y{xIY>Nd~_R$)en7}5lEn8`TAFy?Ns8Y&6{}}WTi`{SCjZq=l zG#kNCMG>HsgF*e2)YFE!QJEKs32bc*Z5~K*B_^;{Ns10Ga*VOp2TB#a*s&w50wTp0 zkPIisAroziyD`Ev7f}hCn82RZm~NFijKHm=F>tkSf$v=vmv;2%hYT{uF>b6O4oCUe z?x3j*(LZhpBK_#gg)P+OZB-FVnwDuv1FJqeSZoE`6TEQ=p#|ZvNMZVbQSx?b@|R@5 z4i-Bmj(G|VpcZc#r`GBRqk400Lp9nmpmJiH_k_csN|sw{i$mM7w2~qvA=f!G0{!4p z5^QawKG_Xq+A|&9o`?~mhB`Hy(Nq=M(=T|aCr)*0c(7KHeqX=ZtZ;M?EuGr1sjf_C zhYy>W!0zi$OknS+32dH;UlwrO_A?hOQ0d*)Hk}-! zfRb9UKyk63mGqsC#*La8L6<5+BgZTB$@5>-0*MK1&e+T3%&ntG6k&smz}b}$6Scyy zx07KP4Y5KZ>qFD3wTwDYu-q`EVAXX4lrObeC5grS+b$w%2Z^eH#SB}=RP9aIgooF2XhXHn`C zh6hv4VzHkr=fPXkjJw(;V`uww(3^UcYZwT_TNhDJ7tB(@VkpQ>pm-pS|EWIYU*B2< zkw_#PEoJj2XS-MqJi2W5VJnlfH%l{! zW`cHR5y?uFhG=eR@dEK^yg9+a`Ofkpf62niI zKwx#_$M4Inen4XOfSObJoOpW#Uy~-`=Yx1+LTGRLnOJ+%B(VGpth;EU*=0*&Q2=?; zOJ^q}6Ao&6=mg~^giU@zkh@MSFs}SyLO@Khh$P=WJ~&zafs7{veuXm|0k^Z*3ixCZ z*tde+D=Ic<27KUlGC*kG3``?2fsLl20hAut#n5$p)7EhiPv|1KH^w(etSHrm`$w~M z6>mlj<7yPbfdZ3_GXhrXqXn=Q6^Z|SG(3Xo&8`?S>&biwv^5cdQ0P9}r|zl~6R=|# zo$4$JZ-n(QEg|VUgyGe+sIL}|e2hUEOSQOsKeDBY8)jL_b%QkM7wvP{lQs;Q6QQavAr%Uavnxv1=EGD zY4pJkB2CXC1O|%@;Bwv86M$mIGAAOBZh+GCNK_ec5=HaRtq0U?c?r zf}w&Wdb)xncqx$~gcq6$(jdJc^>>M|JQ#4nz)ehG>*Q@yUeM$gUkLGiAiga=nrQNf8@Q&%rj)AfQ~H4!zz|xTL7y6|k`9>& zJ(ejAL6352Gi?jm9$cn3R>dPrt5Wpi8?#_85&#rWuPu-qo_YnHw-@ za-^z2hT)Z*|5d7OgL^!hL~5I-$ppxW?^I5F3fus!;#}Q@n#Z;fs%AYxxn8uiq9}J% znwG>Z`lByba5POuvLW}b#yR;b=J=r1Pbjnt=iC`8wugnk(peG*;$ZZBFKsAXj zMxl8o(#4Z`eQ`sC0Q`b92Yf?a*yV_~WGr@$G-K|8Q(edfydkiPF&Jd8h4`~yD^Sa% zN?K|@_AWpdB5FafpHkXVF9XoSF{XTVuqTs3gD-9Qxh$@hONz^#7fKB&vAq$EaECd1 zjf~cq_#NQQ%J)jE_bKbB`LY1&*V7M!wzT;;?SoKemHlYY6$ofkz_kK*RRG_UvVd7D zW`{b_lsW3eT(t}}Ed+0I^~62zKE?2w2R=pHp|l;OiAi_&;?Qd!HV`Z$?bCaEFonhT z#sM9gJ30Roc~bD*AWpk!bETHIdtnjk&(hOn$QO>hjww!z+%g2YjG zYT7YWxLxh&rVU-x0KA-OXH%S*W;>c9ezub-OnS2%OyOg;Z1(VAimd3A-dIo0|7L4s zvW#bKWwPC7X=W}k$@!l@MnUTd@lXwUx)fCNILA@>BENjq{uS&DPj-f z?A4fC%PzOIi8CNl$)Kh>;^+s}0S`8;qoqCs8&@(OGEKE48`mBah1FE33BCPcS>SNgdE z8J@)9bCpB4rjxi1$o)(^isRyyod1aq3$$i({)fXcstZ9|bn)012X5|2&W*|VL=F>l zqbojsS{4d_r`sG@Vn{cN@^{a3h*9pzybCta1NWmvKpel}3 z-NAb|sQd85Ne-NZE+i$13uZPcnp#$31hXEO^uUh=sM!N7_)>f{@XckRBg9AT=7edJ zg`OSd27pTF;S8b&chx3*udruWit0$M>cdkE(}qvZ{~S+c4=ojPmu9UST1$n(_mWg# zst}A#cqiw7$@!n^$0doFoc~S##)1QR0e^=G>EF?!rn<;x5{fC+-rjHTz(FeuXsTc- z>sn@mA<#LvWZhpLE*45yaS#&QV$tIQR#i{C+zcUSS^Z%hmI_|Z=Sz6Y5DWst6)rOO zyV~_4ADq4f5-&3xfwv8`@a^#i&1czC>Rk=kKH`%nI%0C8`2vMq_r)=f(~^_uh#^;9nR_^&%HUch&f@Y` zjum(-Z4gy0%2V4>6bAy;%hN3vJLQNr&zg;@7KiATD;q^YFtB8U_*UW;xu-CL!6R3* zC`5Ps@S-J{iL$~lmg}cS3~st9WMfq?6z9O?RLBLWZXv=8yn<{aCOTq#|Mg(XM*9!j zt9|H^L`O`W+eNHW%eaz)LDyA}&^&OWb#SE};|o@TE45;Ms~=H7a@-|4V#<4Txhc^R z6GhaXVI^5W>iN?W+%~9f4e@qb>^I|$QoYEQ%ZwIihuCS)kShg3_9Z%E%D!!=BFD85 z4}+m*1n19j8w&3nR}zHw#SyVxVx%jS6oodJQe`v6S_Eln(L_fKTCtLEI0ZxjK@ZK! zHIVZ`6DB%hzmq0) zHaslk{wtWJV!bWN9Z}wmP#&;WlNaTC3zNW1Uykn~2iGr1$0+!2+lOpS653^5*MVZVn{SKRcG3sS=>)NJJN3)f{THLpfS zv_oM`(SRC!_XSLIy7jLYz>B3-#o1(rO_n?}{}CK7Is*2>U0 zCI#VH2O$h)2rHv#NL!EJh{eIuS-d(=IZG%dUz+28P0Q)I8G{J1UTMbU(TA(4a9g=; zapO`(lRw>|KNGAKny$JMg)gmM$khvZ-x8WL&R|NVCS0!C@NSnE8v_+_Sy?Aj7%4#3 z#0ovTD!`5AlWsv9NU7y^4UtF((wS7L9=r4?%_bUL-N4xgvw*I`APXr7&^8B_$${7w z@iYkqrT-f34}dk^^ToXq+~txJkeq?sn(D}RMJ^R_BP=X4&i%72L<>Neo!!Cxv+Vc#V{oiu9GH1{-aGUM3(yLn541`BV6K~ zymnO}xeC`*AXh|=Qf?}7^+no`oMsDUEimB#C}PquBRG*3!y>K`t0Q?Sl~$8%YE!L` z1qG!h1E9t&3Sbn2qcNzRhjCJ^w>#|K@Y7>^*?@9w2C|5`>Q;Px`zBGRF@TBVR+^@c zZ^(WDGBd$}y5|din3<@SlMyGaCDY582IAa;Ut)FLd`JhNlxp}@zu9Xpr69hYLq&gFD}@O>Nxcy{c( z9RK4|?btaA_ixs3{2qPjk7n=>)8Fb{Y`YyhZ=dF#9{%^H`EQ%%eh}`X=4yMfq$ zj19W~1?~pQ_W|qA#(ieaLK&;{KLqZF5&nAXe+1l15T5Ny|5M=}4e~!7?!E&Tnh`WQ z{X5{E4E(#{erXHR!<+sW;JqdAUkUeoq|Xkd|HW|MWyjw_xUYfx1cc{zME~pI{u}t; zVci9|m&{ve@VSV88SXd3-G?{BZ^64BPrXKY%;4Z8eDbBrYqMOxu&&rlR1xbX||ki zr6Q#Z%y~nNVyS*^v3!9LtECDrYAIQW|G6l0Lu0(^s201#`_;=&`dD~Dz*b52)B>W{ z;KsZz(bMTDaTm)%{Mq$NPysbZcT7!SB_6erighmG;sB{=G8rX*so=S`)viB6PJ1d# z$}dC-Vvb2nPmTf;;n&XcdwNKMC=m<4I{NVW_EgPSEtxm^sT1&iipxYUS7>!l$H1%3CM?Ow)I rZ>SW@&3%GS#}MPC4{Root>!B3T&aKpP_RNoC^Gq|o`?zU&5-?fN?3oA literal 0 HcmV?d00001 diff --git a/tool/mbl/grub/grub.py b/tool/mbl/grub/grub.py new file mode 100644 index 0000000..2a8000f --- /dev/null +++ b/tool/mbl/grub/grub.py @@ -0,0 +1,284 @@ +# +# ---------- header ----------------------------------------------------------- +# +# project kaneton +# +# license kaneton +# +# file /home/mycure/kaneton/tool/mbl/grub/grub.py +# +# created julien quintard [tue jun 26 11:33:57 2007] +# updated julien quintard [sat mar 5 09:19:34 2011] +# + +# +# ---------- information ------------------------------------------------------ +# +# this script is used to build and install the kaneton microkernel boot +# device. +# + +# +# ---------- imports ---------------------------------------------------------- +# + +import env + +import sys +import re + +# +# ---------- globals ---------------------------------------------------------- +# + +g_image="data/kaneton.img" +g_menu = None +g_components = None +g_action = None + +# +# ---------- functions -------------------------------------------------------- +# + +# +# usage() +# +# this function displays the usage. +# +def usage(): + env.display(env.HEADER_ERROR, "usage: grub.py [action]", env.OPTION_NONE) + + env.display(env.HEADER_NONE, "", env.OPTION_NONE) + + env.display(env.HEADER_ERROR, "actions:", env.OPTION_NONE) + env.display(env.HEADER_ERROR, " build", env.OPTION_NONE) + env.display(env.HEADER_ERROR, " install", env.OPTION_NONE) + + + +# +# warning() +# +# this function warns the user about its configuration. +# +def warning(): + env.display(env.HEADER_OK, "configuration:", env.OPTION_NONE) + env.display(env.HEADER_OK, + " bootmode: " + env._BOOT_MODE_, + env.OPTION_NONE) + env.display(env.HEADER_OK, + " address: " + env._ADDRESS_, + env.OPTION_NONE) + env.display(env.HEADER_OK, + " tftp address: " + env._TFTP_ADDRESS_, + env.OPTION_NONE) + env.display(env.HEADER_OK, + " tftp directory: " + env._TFTP_DIRECTORY_, + env.OPTION_NONE) + env.display(env.HEADER_OK, + " UNIX device: " + env._UDEVICE_, + env.OPTION_NONE) + env.display(env.HEADER_OK, + " mtool device: " + env._MDEVICE_, + env.OPTION_NONE) + env.display(env.HEADER_OK, + " image path: " + env._IMAGE_, + env.OPTION_NONE) + + + +# +# menu() +# +# this function generates the grub menu file. +# +def menu(): + global g_components + global g_menu + content = None + component = None + + # initialize the file content. + content = "timeout 0\n" + \ + "title kaneton\n" + + # add information about how to boot. + if (env._BOOT_MODE_ == "peripheral") or (env._BOOT_MODE_ == "image"): + if env._BOOT_DEVICE_ == "floppy": + content += "root (fd0)\n" + elif env._BOOT_DEVICE_ == "hard-drive": + content += "root (hd0)\n" + else: + env.display(env.HEADER_ERROR, + "unknown boot device '" + env._BOOT_DEVICE_ + "'", + env.OPTION_NONE) + sys.exit(42) + elif env._BOOT_MODE_ == "network": + content += "ifconfig --address=" + env._ADDRESS_ + " --server=" + \ + env._TFTP_ADDRESS_ + "\n" + \ + "root (nd)\n" + else: + env.display(env.HEADER_ERROR, "unknown boot mode '" + env._BOOT_MODE_ + + "'", env.OPTION_NONE) + sys.exit(42) + + # retrieve the grub modules from the _COMPONENTS_ environment variables. + g_components = re.split("[ \t]+", env._COMPONENTS_.strip()) + + # set the first component as the grub kernel. + content += re.sub("^.*\/", "kernel /modules/", g_components[0]) + "\n" + + # set the other components as grub modules. + for component in g_components[1:]: + content += re.sub("^.*\/", "module /modules/", component) + "\n" + + # create the temporary file and fill it. + g_menu = env.temporary(env.OPTION_FILE) + + env.push(g_menu, content, env.OPTION_NONE) + + + +# +# install() +# +# this function installs the kaneton binaries onto the boot device. +# +def install(): + component = None + + # warn the user before performing any action. + warning() + + # display some stuff. + env.display(env.HEADER_NONE, "", env.OPTION_NONE) + env.display(env.HEADER_OK, + "installing kaneton binaries on the boot device", + env.OPTION_NONE) + + # generates the grub menu file. + menu() + + # depending on the boot mode and boot device, install the kaneton binaries + # and the grub menu file. + if env._BOOT_MODE_ == "peripheral": + if env.load(g_menu, env._MDEVICE_, "/boot/grub/menu.lst", + env.OPTION_DEVICE) != 0: + env.display(env.HEADER_ERROR, + "unable to load the menu.lst file", + env.OPTION_NONE) + sys.exit(42) + + for component in g_components: + if not env.path(component, env.OPTION_EXIST): + env.display(env.HEADER_ERROR, " " + component, env.OPTION_NONE) + else: + if env.load(component, env._MDEVICE_, "/modules/", + env.OPTION_DEVICE) != 0: + env.display(env.HEADER_ERROR, + "unable to load the component '" + component + "'", + env.OPTION_NONE) + sys.exit(42) + + env.display(env.HEADER_OK, " " + component, env.OPTION_NONE) + elif env._BOOT_MODE_ == "network": + if env.load(g_menu, env._MDEVICE_, "/boot/grub/menu.lst", + env.OPTION_DEVICE) != 0: + env.display(env.HEADER_ERROR, + "unable to load the menu.lst file", + env.OPTION_NONE) + sys.exit(42) + + for component in g_components: + if not env.path(component, env.OPTION_EXIST): + env.display(env.HEADER_ERROR, " " + component, env.OPTION_NONE) + else: + env.copy(component, env._TFTP_DIRECTORY_, env.OPTION_NONE) + env.display(env.HEADER_OK, " " + component, env.OPTION_NONE) + elif env._BOOT_MODE_ == "image": + if env.load(g_menu, env._IMAGE_, "/boot/grub/menu.lst", + env.OPTION_IMAGE) != 0: + env.display(env.HEADER_ERROR, + "unable to load the menu.lst file", + env.OPTION_NONE) + sys.exit(42) + + for component in g_components: + if not env.path(component, env.OPTION_EXIST): + env.display(env.HEADER_ERROR, " " + component, env.OPTION_NONE) + else: + if env.load(component, env._IMAGE_, "/modules/", + env.OPTION_IMAGE) != 0: + env.display(env.HEADER_ERROR, + "unable to load the component '" + component + "'", + env.OPTION_NONE) + sys.exit(42) + + env.display(env.HEADER_OK, " " + component, env.OPTION_NONE) + else: + env.display(env.HEADER_ERROR, "unknown boot mode '" + env._BOOT_MODE_ + + "'", env.OPTION_NONE) + sys.exit(42) + + + +# +# build() +# +# this function builds, initializes the boot device. +# +def build(): + # warn the user before performing any action. + warning() + + # display some stuff. + env.display(env.HEADER_NONE, "", env.OPTION_NONE) + env.display(env.HEADER_OK, "initializing the boot device", env.OPTION_NONE) + + # for each boot mode, initialize the boot device. + if (env._BOOT_MODE_ == "peripheral") or (env._BOOT_MODE_ == "network"): + env.push(env._UDEVICE_, + env.pull(g_image, env.OPTION_NONE), + env.OPTION_NONE) + elif env._BOOT_MODE_ == "image": + env.copy(g_image, env._IMAGE_, env.OPTION_NONE) + else: + env.display(env.HEADER_ERROR, "unknown boot mode '" + env._BOOT_MODE_ + + "'", env.OPTION_NONE) + sys.exit(42) + + + +# +# main() +# +# this function performs the main work. +# +def main(): + global g_action + + # check the number of arguments. + if len(sys.argv) != 2: + usage() + sys.exit(42) + + # set the action. + g_action = sys.argv[1] + + # act according to the action argument. + if g_action == "build": + build() + elif g_action == "install": + install() + else: + env.display(env.HEADER_ERROR, "unknown action \'" + g_action + "'", + env.OPTION_NONE) + usage() + sys.exit(42) + +# +# ---------- entry point ------------------------------------------------------ +# + +if __name__ == "__main__": + main() diff --git a/tool/mkp/mkp.py b/tool/mkp/mkp.py new file mode 100644 index 0000000..eeb9105 --- /dev/null +++ b/tool/mkp/mkp.py @@ -0,0 +1,239 @@ +# +# Makeprotos / mkp.py +# Made by pwipwi +# This python script was created to +# 1) Give me an opportunity to practice python a bit :) +# 2) Replace old mkp.pl, which was sufficient, but a bit unreadable. +# +# usage: +# ./mkp.py header1 header2 ... +# +# For this script to work efficiently, you have a few things to do : +# * Code with the Epita CSS, at least in function declaration. +# This little script is far from being a C parser, so it will +# exclusively recognize Epita CSS' functions declarations +# * You have to end your prototypes section by a +# /* +# * eop +# */ +# statement +# * Last but not least, you need to have a VALID prototypes secion. +# it looks like that +# /* +# * ---------- prototypes -------------------- +# * +# * ./relative/path/to/file/1 +# * ./relative/path/to/file/2 +# * +# * ./et/caetera +# */ +# Be sure to respect the spacing, as the script is especially not +# tolerant with them (especially before the *). +# You can have as much spaces as you want between the * and the file +# names, though. +# +# That should do the trick. If you ever encounter a problem which is most +# likely a bug in that script, please send me a mail. +# + +# System module +import sys +# OS specific module +import os +# Regular Expression module +import re + + +## --------- Classes ---------------------------------------------------------- + +class cl_functions: + """ cl_functions : functions definitions + This class is the one that shall hold all the function definitions + we will have found in the diverse files. + """ + def __init__(self): + self.di_files = {} # initialize file dictionnary + self.beginning = [] + self.ending = [] + self.filenames = [] + + def add_filename(self, str_filename): + self.di_files[str_filename] = [] + + def add_function(self, str_filename, li_functiondecl): + self.di_files[str_filename].append(li_functiondecl) + + def purge(self): + self.di_files = {} + + + +## --------- functions --------------------------------------------------------- + +## +## error +## +def fn_error(string, code): + """Throw an Error + Print an error and exit the program with the corresponding code + """ + print(sys.argv[0] + ':' + string) + sys.exit(code) + +## +## readprotosfiles +## +def fn_readprotosfiles(fp, functions): + """Readprotos files + Read the file names of the files that shall be processed + """ + filelist = [] + line = fp.readline() + while line != '' and not re.match(' \* -+ prototypes -+', line): + functions.beginning.append(line) + line = fp.readline() + if line == '': + fn_error('this file doesn\'t contain a valid prototype section', 3) + # iterate until the end of the prototypes section + endre = re.compile('.*\*/') + filere = re.compile('\s*\*+\s+[./_a-zA-Z0-9]+') + filenamere = re.compile('[-./_a-zA-Z0-9]+') + eopre = re.compile('\s*\*+\s+eop') + while line != '' and not endre.match(line): + functions.beginning.append(line) + if filere.match(line): + line = filenamere.search(line).group() + filelist.append(line) + line = fp.readline() + functions.beginning.append(line) + while line != '' and not eopre.match(line): + line = fp.readline() + while line != '' and not endre.match(line): + line = fp.readline() + if line == '': + fn_error("no eop in" + fp.filename, 3) + while line != '': + line = fp.readline() + functions.ending.append(line) + del filere + del endre + del filenamere + return filelist + +## +## processfile +## +def fn_processfile(filename, functions): + """Process files + returns a function list from the given file + """ + inside_funcion = 0 + inside_comment = 0 + currentlist = [] + + try: + fp = open(filename, 'r') + except IOError: + fn_error("unable to open" + filename, 4) + functionre = re.compile('^[a-zA-Z0-9_](([\*\w]*)\s+\**)+\w+\(') # function declaration + commentre = re.compile('/\*') # comment start + endcomre = re.compile('\*/') # comment end + staticre = re.compile('static') # static keyword + for line in fp.readlines(): + matched = functionre.search(line) + commatched = commentre.search(line) + endcommatched = endcomre.search(line) + if commatched: + inside_comment = inside_comment + 1 + if endcommatched: + inside_comment = inside_comment - 1 + if matched and not inside_comment: + inside_funcion = 1 + if inside_funcion: + static_match = staticre.search(line) + if re.search('\)\n', line) and not static_match: + line = re.match("(.*)$", line).group(0) + line = line + ';\n' + currentlist.append(line) + functions.add_function(filename, currentlist) + currentlist = [] + inside_funcion = 0 + continue + else: + if not static_match: + currentlist.append(line) + else: + inside_funcion = 0 + continue + fp.close() + + +## +## +## +def fn_write_header(functions, filename): + """Write the header + + Write the function declarations to the header files + """ + try: + fp = open(filename, 'w') + except IOError: + fn_error(filename + " is not writeable") + fp.writelines(functions.beginning) + for file in functions.filenames: + fp.write('\n/*\n * ' + file + '\n */\n\n') + for func in functions.di_files[file]: + fp.writelines(func) + fp.write('\n') + fp.write('\n/*\n * eop\n */\n') + fp.writelines(functions.ending) + fp.close() + +## +## mkprotos +## +def fn_mkprotos(path): + """ Make protos + This function calls all the others. It proceeds in that order: + first, it calls fn_readprotos to get a list of the files to parse + and recover prototypes. + then, for each of those files, it calls fn_processfile, which + parses the file and gets the prototypes. + finally, it calls fn_write_header to actually write the header + """ + functions = cl_functions() + currentpwd = os.getcwd() + filename = '' + + if (path.count('/') >= 1): # is it + filename = re.sub('.*/', '', path) # splits the filename from the path + dirname = re.sub('/[^/]*$', '', path) # splits the directory name from the path + try: + os.chdir(dirname) + except OSError: + fn_error(dirname + ', no such file or directory !', 1) + else: + filename = path + try: + fp = open(filename, 'r') + except IOError: + fn_error(filename + ', no such file', 2) + functions.filenames = fn_readprotosfiles(fp, functions) + for file in functions.filenames: + functions.add_filename(file) + fn_processfile(file, functions) + fp.close() + fn_write_header(functions, filename) + os.chdir(currentpwd) + return functions + + +## +## \o/ Main function \o/ +## +args = sys.argv[1:] +if len(args) == 0: + fn_error('Give at least 1 argument, you silly man !', 3) +for file in args: + functions = fn_mkprotos(file)